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
511 test "doesn't return activities with filtered words" do
512 user = insert(:user)
513 user_two = insert(:user)
514 insert(:filter, user: user, phrase: "test", hide: true)
515
516 {:ok, %{id: id1, data: %{"context" => context}}} = CommonAPI.post(user, %{status: "1"})
517
518 {:ok, %{id: id2}} = CommonAPI.post(user_two, %{status: "2", in_reply_to_status_id: id1})
519
520 {:ok, %{id: id3} = user_activity} =
521 CommonAPI.post(user, %{status: "3 test?", in_reply_to_status_id: id2})
522
523 {:ok, %{id: id4} = filtered_activity} =
524 CommonAPI.post(user_two, %{status: "4 test!", in_reply_to_status_id: id3})
525
526 {:ok, _} = CommonAPI.post(user, %{status: "5", in_reply_to_status_id: id4})
527
528 activities =
529 context
530 |> ActivityPub.fetch_activities_for_context(%{user: user})
531 |> Enum.map(& &1.id)
532
533 assert length(activities) == 4
534 assert user_activity.id in activities
535 refute filtered_activity.id in activities
536 end
537 end
538
539 test "doesn't return blocked activities" do
540 activity_one = insert(:note_activity)
541 activity_two = insert(:note_activity)
542 activity_three = insert(:note_activity)
543 user = insert(:user)
544 booster = insert(:user)
545 {:ok, _user_relationship} = User.block(user, %{ap_id: activity_one.data["actor"]})
546
547 activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
548
549 assert Enum.member?(activities, activity_two)
550 assert Enum.member?(activities, activity_three)
551 refute Enum.member?(activities, activity_one)
552
553 {:ok, _user_block} = User.unblock(user, %{ap_id: activity_one.data["actor"]})
554
555 activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
556
557 assert Enum.member?(activities, activity_two)
558 assert Enum.member?(activities, activity_three)
559 assert Enum.member?(activities, activity_one)
560
561 {:ok, _user_relationship} = User.block(user, %{ap_id: activity_three.data["actor"]})
562 {:ok, %{data: %{"object" => id}}} = CommonAPI.repeat(activity_three.id, booster)
563 %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
564 activity_three = Activity.get_by_id(activity_three.id)
565
566 activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
567
568 assert Enum.member?(activities, activity_two)
569 refute Enum.member?(activities, activity_three)
570 refute Enum.member?(activities, boost_activity)
571 assert Enum.member?(activities, activity_one)
572
573 activities = ActivityPub.fetch_activities([], %{blocking_user: nil, skip_preload: true})
574
575 assert Enum.member?(activities, activity_two)
576 assert Enum.member?(activities, activity_three)
577 assert Enum.member?(activities, boost_activity)
578 assert Enum.member?(activities, activity_one)
579 end
580
581 test "doesn't return transitive interactions concerning blocked users" do
582 blocker = insert(:user)
583 blockee = insert(:user)
584 friend = insert(:user)
585
586 {:ok, _user_relationship} = User.block(blocker, blockee)
587
588 {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey!"})
589
590 {:ok, activity_two} = CommonAPI.post(friend, %{status: "hey! @#{blockee.nickname}"})
591
592 {:ok, activity_three} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})
593
594 {:ok, activity_four} = CommonAPI.post(blockee, %{status: "hey! @#{blocker.nickname}"})
595
596 activities = ActivityPub.fetch_activities([], %{blocking_user: blocker})
597
598 assert Enum.member?(activities, activity_one)
599 refute Enum.member?(activities, activity_two)
600 refute Enum.member?(activities, activity_three)
601 refute Enum.member?(activities, activity_four)
602 end
603
604 test "doesn't return announce activities with blocked users in 'to'" do
605 blocker = insert(:user)
606 blockee = insert(:user)
607 friend = insert(:user)
608
609 {:ok, _user_relationship} = User.block(blocker, blockee)
610
611 {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey!"})
612
613 {:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})
614
615 {:ok, activity_three} = CommonAPI.repeat(activity_two.id, friend)
616
617 activities =
618 ActivityPub.fetch_activities([], %{blocking_user: blocker})
619 |> Enum.map(fn act -> act.id end)
620
621 assert Enum.member?(activities, activity_one.id)
622 refute Enum.member?(activities, activity_two.id)
623 refute Enum.member?(activities, activity_three.id)
624 end
625
626 test "doesn't return announce activities with blocked users in 'cc'" do
627 blocker = insert(:user)
628 blockee = insert(:user)
629 friend = insert(:user)
630
631 {:ok, _user_relationship} = User.block(blocker, blockee)
632
633 {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey!"})
634
635 {:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})
636
637 assert object = Pleroma.Object.normalize(activity_two)
638
639 data = %{
640 "actor" => friend.ap_id,
641 "object" => object.data["id"],
642 "context" => object.data["context"],
643 "type" => "Announce",
644 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
645 "cc" => [blockee.ap_id]
646 }
647
648 assert {:ok, activity_three} = ActivityPub.insert(data)
649
650 activities =
651 ActivityPub.fetch_activities([], %{blocking_user: blocker})
652 |> Enum.map(fn act -> act.id end)
653
654 assert Enum.member?(activities, activity_one.id)
655 refute Enum.member?(activities, activity_two.id)
656 refute Enum.member?(activities, activity_three.id)
657 end
658
659 test "doesn't return activities from blocked domains" do
660 domain = "dogwhistle.zone"
661 domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"})
662 note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})
663 activity = insert(:note_activity, %{note: note})
664 user = insert(:user)
665 {:ok, user} = User.block_domain(user, domain)
666
667 activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
668
669 refute activity in activities
670
671 followed_user = insert(:user)
672 CommonAPI.follow(user, followed_user)
673 {:ok, repeat_activity} = CommonAPI.repeat(activity.id, followed_user)
674
675 activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
676
677 refute repeat_activity in activities
678 end
679
680 test "does return activities from followed users on blocked domains" do
681 domain = "meanies.social"
682 domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"})
683 blocker = insert(:user)
684
685 {:ok, blocker} = User.follow(blocker, domain_user)
686 {:ok, blocker} = User.block_domain(blocker, domain)
687
688 assert User.following?(blocker, domain_user)
689 assert User.blocks_domain?(blocker, domain_user)
690 refute User.blocks?(blocker, domain_user)
691
692 note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})
693 activity = insert(:note_activity, %{note: note})
694
695 activities = ActivityPub.fetch_activities([], %{blocking_user: blocker, skip_preload: true})
696
697 assert activity in activities
698
699 # And check that if the guy we DO follow boosts someone else from their domain,
700 # that should be hidden
701 another_user = insert(:user, %{ap_id: "https://#{domain}/@meanie2"})
702 bad_note = insert(:note, %{data: %{"actor" => another_user.ap_id}})
703 bad_activity = insert(:note_activity, %{note: bad_note})
704 {:ok, repeat_activity} = CommonAPI.repeat(bad_activity.id, domain_user)
705
706 activities = ActivityPub.fetch_activities([], %{blocking_user: blocker, skip_preload: true})
707
708 refute repeat_activity in activities
709 end
710
711 test "doesn't return muted activities" do
712 activity_one = insert(:note_activity)
713 activity_two = insert(:note_activity)
714 activity_three = insert(:note_activity)
715 user = insert(:user)
716 booster = insert(:user)
717
718 activity_one_actor = User.get_by_ap_id(activity_one.data["actor"])
719 {:ok, _user_relationships} = User.mute(user, activity_one_actor)
720
721 activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
722
723 assert Enum.member?(activities, activity_two)
724 assert Enum.member?(activities, activity_three)
725 refute Enum.member?(activities, activity_one)
726
727 # Calling with 'with_muted' will deliver muted activities, too.
728 activities =
729 ActivityPub.fetch_activities([], %{
730 muting_user: user,
731 with_muted: true,
732 skip_preload: true
733 })
734
735 assert Enum.member?(activities, activity_two)
736 assert Enum.member?(activities, activity_three)
737 assert Enum.member?(activities, activity_one)
738
739 {:ok, _user_mute} = User.unmute(user, activity_one_actor)
740
741 activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
742
743 assert Enum.member?(activities, activity_two)
744 assert Enum.member?(activities, activity_three)
745 assert Enum.member?(activities, activity_one)
746
747 activity_three_actor = User.get_by_ap_id(activity_three.data["actor"])
748 {:ok, _user_relationships} = User.mute(user, activity_three_actor)
749 {:ok, %{data: %{"object" => id}}} = CommonAPI.repeat(activity_three.id, booster)
750 %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
751 activity_three = Activity.get_by_id(activity_three.id)
752
753 activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
754
755 assert Enum.member?(activities, activity_two)
756 refute Enum.member?(activities, activity_three)
757 refute Enum.member?(activities, boost_activity)
758 assert Enum.member?(activities, activity_one)
759
760 activities = ActivityPub.fetch_activities([], %{muting_user: nil, skip_preload: true})
761
762 assert Enum.member?(activities, activity_two)
763 assert Enum.member?(activities, activity_three)
764 assert Enum.member?(activities, boost_activity)
765 assert Enum.member?(activities, activity_one)
766 end
767
768 test "doesn't return thread muted activities" do
769 user = insert(:user)
770 _activity_one = insert(:note_activity)
771 note_two = insert(:note, data: %{"context" => "suya.."})
772 activity_two = insert(:note_activity, note: note_two)
773
774 {:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
775
776 assert [_activity_one] = ActivityPub.fetch_activities([], %{muting_user: user})
777 end
778
779 test "returns thread muted activities when with_muted is set" do
780 user = insert(:user)
781 _activity_one = insert(:note_activity)
782 note_two = insert(:note, data: %{"context" => "suya.."})
783 activity_two = insert(:note_activity, note: note_two)
784
785 {:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
786
787 assert [_activity_two, _activity_one] =
788 ActivityPub.fetch_activities([], %{muting_user: user, with_muted: true})
789 end
790
791 test "does include announces on request" do
792 activity_three = insert(:note_activity)
793 user = insert(:user)
794 booster = insert(:user)
795
796 {:ok, user} = User.follow(user, booster)
797
798 {:ok, announce} = CommonAPI.repeat(activity_three.id, booster)
799
800 [announce_activity] = ActivityPub.fetch_activities([user.ap_id | User.following(user)])
801
802 assert announce_activity.id == announce.id
803 end
804
805 test "excludes reblogs on request" do
806 user = insert(:user)
807 {:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Create"}, %{:user => user})
808 {:ok, _} = ActivityBuilder.insert(%{"type" => "Announce"}, %{:user => user})
809
810 [activity] = ActivityPub.fetch_user_activities(user, nil, %{exclude_reblogs: true})
811
812 assert activity == expected_activity
813 end
814
815 describe "irreversible filters" do
816 setup do
817 user = insert(:user)
818 user_two = insert(:user)
819
820 insert(:filter, user: user_two, phrase: "cofe", hide: true)
821 insert(:filter, user: user_two, phrase: "ok boomer", hide: true)
822 insert(:filter, user: user_two, phrase: "test", hide: false)
823
824 params = %{
825 type: ["Create", "Announce"],
826 user: user_two
827 }
828
829 {:ok, %{user: user, user_two: user_two, params: params}}
830 end
831
832 test "it returns statuses if they don't contain exact filter words", %{
833 user: user,
834 params: params
835 } do
836 {:ok, _} = CommonAPI.post(user, %{status: "hey"})
837 {:ok, _} = CommonAPI.post(user, %{status: "got cofefe?"})
838 {:ok, _} = CommonAPI.post(user, %{status: "I am not a boomer"})
839 {:ok, _} = CommonAPI.post(user, %{status: "ok boomers"})
840 {:ok, _} = CommonAPI.post(user, %{status: "ccofee is not a word"})
841 {:ok, _} = CommonAPI.post(user, %{status: "this is a test"})
842
843 activities = ActivityPub.fetch_activities([], params)
844
845 assert Enum.count(activities) == 6
846 end
847
848 test "it does not filter user's own statuses", %{user_two: user_two, params: params} do
849 {:ok, _} = CommonAPI.post(user_two, %{status: "Give me some cofe!"})
850 {:ok, _} = CommonAPI.post(user_two, %{status: "ok boomer"})
851
852 activities = ActivityPub.fetch_activities([], params)
853
854 assert Enum.count(activities) == 2
855 end
856
857 test "it excludes statuses with filter words", %{user: user, params: params} do
858 {:ok, _} = CommonAPI.post(user, %{status: "Give me some cofe!"})
859 {:ok, _} = CommonAPI.post(user, %{status: "ok boomer"})
860 {:ok, _} = CommonAPI.post(user, %{status: "is it a cOfE?"})
861 {:ok, _} = CommonAPI.post(user, %{status: "cofe is all I need"})
862 {:ok, _} = CommonAPI.post(user, %{status: "— ok BOOMER\n"})
863
864 activities = ActivityPub.fetch_activities([], params)
865
866 assert Enum.empty?(activities)
867 end
868
869 test "it returns all statuses if user does not have any filters" do
870 another_user = insert(:user)
871 {:ok, _} = CommonAPI.post(another_user, %{status: "got cofe?"})
872 {:ok, _} = CommonAPI.post(another_user, %{status: "test!"})
873
874 activities =
875 ActivityPub.fetch_activities([], %{
876 type: ["Create", "Announce"],
877 user: another_user
878 })
879
880 assert Enum.count(activities) == 2
881 end
882 end
883
884 describe "public fetch activities" do
885 test "doesn't retrieve unlisted activities" do
886 user = insert(:user)
887
888 {:ok, _unlisted_activity} = CommonAPI.post(user, %{status: "yeah", visibility: "unlisted"})
889
890 {:ok, listed_activity} = CommonAPI.post(user, %{status: "yeah"})
891
892 [activity] = ActivityPub.fetch_public_activities()
893
894 assert activity == listed_activity
895 end
896
897 test "retrieves public activities" do
898 _activities = ActivityPub.fetch_public_activities()
899
900 %{public: public} = ActivityBuilder.public_and_non_public()
901
902 activities = ActivityPub.fetch_public_activities()
903 assert length(activities) == 1
904 assert Enum.at(activities, 0) == public
905 end
906
907 test "retrieves a maximum of 20 activities" do
908 ActivityBuilder.insert_list(10)
909 expected_activities = ActivityBuilder.insert_list(20)
910
911 activities = ActivityPub.fetch_public_activities()
912
913 assert collect_ids(activities) == collect_ids(expected_activities)
914 assert length(activities) == 20
915 end
916
917 test "retrieves ids starting from a since_id" do
918 activities = ActivityBuilder.insert_list(30)
919 expected_activities = ActivityBuilder.insert_list(10)
920 since_id = List.last(activities).id
921
922 activities = ActivityPub.fetch_public_activities(%{since_id: since_id})
923
924 assert collect_ids(activities) == collect_ids(expected_activities)
925 assert length(activities) == 10
926 end
927
928 test "retrieves ids up to max_id" do
929 ActivityBuilder.insert_list(10)
930 expected_activities = ActivityBuilder.insert_list(20)
931
932 %{id: max_id} =
933 10
934 |> ActivityBuilder.insert_list()
935 |> List.first()
936
937 activities = ActivityPub.fetch_public_activities(%{max_id: max_id})
938
939 assert length(activities) == 20
940 assert collect_ids(activities) == collect_ids(expected_activities)
941 end
942
943 test "paginates via offset/limit" do
944 _first_part_activities = ActivityBuilder.insert_list(10)
945 second_part_activities = ActivityBuilder.insert_list(10)
946
947 later_activities = ActivityBuilder.insert_list(10)
948
949 activities = ActivityPub.fetch_public_activities(%{page: "2", page_size: "20"}, :offset)
950
951 assert length(activities) == 20
952
953 assert collect_ids(activities) ==
954 collect_ids(second_part_activities) ++ collect_ids(later_activities)
955 end
956
957 test "doesn't return reblogs for users for whom reblogs have been muted" do
958 activity = insert(:note_activity)
959 user = insert(:user)
960 booster = insert(:user)
961 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)
962
963 {:ok, activity} = CommonAPI.repeat(activity.id, booster)
964
965 activities = ActivityPub.fetch_activities([], %{muting_user: user})
966
967 refute Enum.any?(activities, fn %{id: id} -> id == activity.id end)
968 end
969
970 test "returns reblogs for users for whom reblogs have not been muted" do
971 activity = insert(:note_activity)
972 user = insert(:user)
973 booster = insert(:user)
974 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)
975 {:ok, _reblog_mute} = CommonAPI.show_reblogs(user, booster)
976
977 {:ok, activity} = CommonAPI.repeat(activity.id, booster)
978
979 activities = ActivityPub.fetch_activities([], %{muting_user: user})
980
981 assert Enum.any?(activities, fn %{id: id} -> id == activity.id end)
982 end
983 end
984
985 describe "uploading files" do
986 test "copies the file to the configured folder" do
987 file = %Plug.Upload{
988 content_type: "image/jpg",
989 path: Path.absname("test/fixtures/image.jpg"),
990 filename: "an_image.jpg"
991 }
992
993 {:ok, %Object{} = object} = ActivityPub.upload(file)
994 assert object.data["name"] == "an_image.jpg"
995 end
996
997 test "works with base64 encoded images" do
998 file = %{
999 img: data_uri()
1000 }
1001
1002 {:ok, %Object{}} = ActivityPub.upload(file)
1003 end
1004 end
1005
1006 describe "fetch the latest Follow" do
1007 test "fetches the latest Follow activity" do
1008 %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
1009 follower = Repo.get_by(User, ap_id: activity.data["actor"])
1010 followed = Repo.get_by(User, ap_id: activity.data["object"])
1011
1012 assert activity == Utils.fetch_latest_follow(follower, followed)
1013 end
1014 end
1015
1016 describe "unfollowing" do
1017 test "it reverts unfollow activity" do
1018 follower = insert(:user)
1019 followed = insert(:user)
1020
1021 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed)
1022
1023 with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
1024 assert {:error, :reverted} = ActivityPub.unfollow(follower, followed)
1025 end
1026
1027 activity = Activity.get_by_id(follow_activity.id)
1028 assert activity.data["type"] == "Follow"
1029 assert activity.data["actor"] == follower.ap_id
1030
1031 assert activity.data["object"] == followed.ap_id
1032 end
1033
1034 test "creates an undo activity for the last follow" do
1035 follower = insert(:user)
1036 followed = insert(:user)
1037
1038 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed)
1039 {:ok, activity} = ActivityPub.unfollow(follower, followed)
1040
1041 assert activity.data["type"] == "Undo"
1042 assert activity.data["actor"] == follower.ap_id
1043
1044 embedded_object = activity.data["object"]
1045 assert is_map(embedded_object)
1046 assert embedded_object["type"] == "Follow"
1047 assert embedded_object["object"] == followed.ap_id
1048 assert embedded_object["id"] == follow_activity.data["id"]
1049 end
1050
1051 test "creates an undo activity for a pending follow request" do
1052 follower = insert(:user)
1053 followed = insert(:user, %{locked: true})
1054
1055 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed)
1056 {:ok, activity} = ActivityPub.unfollow(follower, followed)
1057
1058 assert activity.data["type"] == "Undo"
1059 assert activity.data["actor"] == follower.ap_id
1060
1061 embedded_object = activity.data["object"]
1062 assert is_map(embedded_object)
1063 assert embedded_object["type"] == "Follow"
1064 assert embedded_object["object"] == followed.ap_id
1065 assert embedded_object["id"] == follow_activity.data["id"]
1066 end
1067 end
1068
1069 describe "timeline post-processing" do
1070 test "it filters broken threads" do
1071 user1 = insert(:user)
1072 user2 = insert(:user)
1073 user3 = insert(:user)
1074
1075 {:ok, user1} = User.follow(user1, user3)
1076 assert User.following?(user1, user3)
1077
1078 {:ok, user2} = User.follow(user2, user3)
1079 assert User.following?(user2, user3)
1080
1081 {:ok, user3} = User.follow(user3, user2)
1082 assert User.following?(user3, user2)
1083
1084 {:ok, public_activity} = CommonAPI.post(user3, %{status: "hi 1"})
1085
1086 {:ok, private_activity_1} = CommonAPI.post(user3, %{status: "hi 2", visibility: "private"})
1087
1088 {:ok, private_activity_2} =
1089 CommonAPI.post(user2, %{
1090 status: "hi 3",
1091 visibility: "private",
1092 in_reply_to_status_id: private_activity_1.id
1093 })
1094
1095 {:ok, private_activity_3} =
1096 CommonAPI.post(user3, %{
1097 status: "hi 4",
1098 visibility: "private",
1099 in_reply_to_status_id: private_activity_2.id
1100 })
1101
1102 activities =
1103 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)])
1104 |> Enum.map(fn a -> a.id end)
1105
1106 private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"])
1107
1108 assert [public_activity.id, private_activity_1.id, private_activity_3.id] == activities
1109
1110 assert length(activities) == 3
1111
1112 activities =
1113 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)], %{user: user1})
1114 |> Enum.map(fn a -> a.id end)
1115
1116 assert [public_activity.id, private_activity_1.id] == activities
1117 assert length(activities) == 2
1118 end
1119 end
1120
1121 describe "flag/1" do
1122 setup do
1123 reporter = insert(:user)
1124 target_account = insert(:user)
1125 content = "foobar"
1126 {:ok, activity} = CommonAPI.post(target_account, %{status: content})
1127 context = Utils.generate_context_id()
1128
1129 reporter_ap_id = reporter.ap_id
1130 target_ap_id = target_account.ap_id
1131 activity_ap_id = activity.data["id"]
1132
1133 activity_with_object = Activity.get_by_ap_id_with_object(activity_ap_id)
1134
1135 {:ok,
1136 %{
1137 reporter: reporter,
1138 context: context,
1139 target_account: target_account,
1140 reported_activity: activity,
1141 content: content,
1142 activity_ap_id: activity_ap_id,
1143 activity_with_object: activity_with_object,
1144 reporter_ap_id: reporter_ap_id,
1145 target_ap_id: target_ap_id
1146 }}
1147 end
1148
1149 test "it can create a Flag activity",
1150 %{
1151 reporter: reporter,
1152 context: context,
1153 target_account: target_account,
1154 reported_activity: reported_activity,
1155 content: content,
1156 activity_ap_id: activity_ap_id,
1157 activity_with_object: activity_with_object,
1158 reporter_ap_id: reporter_ap_id,
1159 target_ap_id: target_ap_id
1160 } do
1161 assert {:ok, activity} =
1162 ActivityPub.flag(%{
1163 actor: reporter,
1164 context: context,
1165 account: target_account,
1166 statuses: [reported_activity],
1167 content: content
1168 })
1169
1170 note_obj = %{
1171 "type" => "Note",
1172 "id" => activity_ap_id,
1173 "content" => content,
1174 "published" => activity_with_object.object.data["published"],
1175 "actor" => AccountView.render("show.json", %{user: target_account})
1176 }
1177
1178 assert %Activity{
1179 actor: ^reporter_ap_id,
1180 data: %{
1181 "type" => "Flag",
1182 "content" => ^content,
1183 "context" => ^context,
1184 "object" => [^target_ap_id, ^note_obj]
1185 }
1186 } = activity
1187 end
1188
1189 test_with_mock "strips status data from Flag, before federating it",
1190 %{
1191 reporter: reporter,
1192 context: context,
1193 target_account: target_account,
1194 reported_activity: reported_activity,
1195 content: content
1196 },
1197 Utils,
1198 [:passthrough],
1199 [] do
1200 {:ok, activity} =
1201 ActivityPub.flag(%{
1202 actor: reporter,
1203 context: context,
1204 account: target_account,
1205 statuses: [reported_activity],
1206 content: content
1207 })
1208
1209 new_data =
1210 put_in(activity.data, ["object"], [target_account.ap_id, reported_activity.data["id"]])
1211
1212 assert_called(Utils.maybe_federate(%{activity | data: new_data}))
1213 end
1214 end
1215
1216 test "fetch_activities/2 returns activities addressed to a list " do
1217 user = insert(:user)
1218 member = insert(:user)
1219 {:ok, list} = Pleroma.List.create("foo", user)
1220 {:ok, list} = Pleroma.List.follow(list, member)
1221
1222 {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
1223
1224 activity = Repo.preload(activity, :bookmark)
1225 activity = %Activity{activity | thread_muted?: !!activity.thread_muted?}
1226
1227 assert ActivityPub.fetch_activities([], %{user: user}) == [activity]
1228 end
1229
1230 def data_uri do
1231 File.read!("test/fixtures/avatar_data_uri")
1232 end
1233
1234 describe "fetch_activities_bounded" do
1235 test "fetches private posts for followed users" do
1236 user = insert(:user)
1237
1238 {:ok, activity} =
1239 CommonAPI.post(user, %{
1240 status: "thought I looked cute might delete later :3",
1241 visibility: "private"
1242 })
1243
1244 [result] = ActivityPub.fetch_activities_bounded([user.follower_address], [])
1245 assert result.id == activity.id
1246 end
1247
1248 test "fetches only public posts for other users" do
1249 user = insert(:user)
1250 {:ok, activity} = CommonAPI.post(user, %{status: "#cofe", visibility: "public"})
1251
1252 {:ok, _private_activity} =
1253 CommonAPI.post(user, %{
1254 status: "why is tenshi eating a corndog so cute?",
1255 visibility: "private"
1256 })
1257
1258 [result] = ActivityPub.fetch_activities_bounded([], [user.follower_address])
1259 assert result.id == activity.id
1260 end
1261 end
1262
1263 describe "fetch_follow_information_for_user" do
1264 test "syncronizes following/followers counters" do
1265 user =
1266 insert(:user,
1267 local: false,
1268 follower_address: "http://localhost:4001/users/fuser2/followers",
1269 following_address: "http://localhost:4001/users/fuser2/following"
1270 )
1271
1272 {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
1273 assert info.follower_count == 527
1274 assert info.following_count == 267
1275 end
1276
1277 test "detects hidden followers" do
1278 mock(fn env ->
1279 case env.url do
1280 "http://localhost:4001/users/masto_closed/followers?page=1" ->
1281 %Tesla.Env{status: 403, body: ""}
1282
1283 _ ->
1284 apply(HttpRequestMock, :request, [env])
1285 end
1286 end)
1287
1288 user =
1289 insert(:user,
1290 local: false,
1291 follower_address: "http://localhost:4001/users/masto_closed/followers",
1292 following_address: "http://localhost:4001/users/masto_closed/following"
1293 )
1294
1295 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1296 assert follow_info.hide_followers == true
1297 assert follow_info.hide_follows == false
1298 end
1299
1300 test "detects hidden follows" do
1301 mock(fn env ->
1302 case env.url do
1303 "http://localhost:4001/users/masto_closed/following?page=1" ->
1304 %Tesla.Env{status: 403, body: ""}
1305
1306 _ ->
1307 apply(HttpRequestMock, :request, [env])
1308 end
1309 end)
1310
1311 user =
1312 insert(:user,
1313 local: false,
1314 follower_address: "http://localhost:4001/users/masto_closed/followers",
1315 following_address: "http://localhost:4001/users/masto_closed/following"
1316 )
1317
1318 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1319 assert follow_info.hide_followers == false
1320 assert follow_info.hide_follows == true
1321 end
1322
1323 test "detects hidden follows/followers for friendica" do
1324 user =
1325 insert(:user,
1326 local: false,
1327 follower_address: "http://localhost:8080/followers/fuser3",
1328 following_address: "http://localhost:8080/following/fuser3"
1329 )
1330
1331 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1332 assert follow_info.hide_followers == true
1333 assert follow_info.follower_count == 296
1334 assert follow_info.following_count == 32
1335 assert follow_info.hide_follows == true
1336 end
1337
1338 test "doesn't crash when follower and following counters are hidden" do
1339 mock(fn env ->
1340 case env.url do
1341 "http://localhost:4001/users/masto_hidden_counters/following" ->
1342 json(%{
1343 "@context" => "https://www.w3.org/ns/activitystreams",
1344 "id" => "http://localhost:4001/users/masto_hidden_counters/followers"
1345 })
1346
1347 "http://localhost:4001/users/masto_hidden_counters/following?page=1" ->
1348 %Tesla.Env{status: 403, body: ""}
1349
1350 "http://localhost:4001/users/masto_hidden_counters/followers" ->
1351 json(%{
1352 "@context" => "https://www.w3.org/ns/activitystreams",
1353 "id" => "http://localhost:4001/users/masto_hidden_counters/following"
1354 })
1355
1356 "http://localhost:4001/users/masto_hidden_counters/followers?page=1" ->
1357 %Tesla.Env{status: 403, body: ""}
1358 end
1359 end)
1360
1361 user =
1362 insert(:user,
1363 local: false,
1364 follower_address: "http://localhost:4001/users/masto_hidden_counters/followers",
1365 following_address: "http://localhost:4001/users/masto_hidden_counters/following"
1366 )
1367
1368 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1369
1370 assert follow_info.hide_followers == true
1371 assert follow_info.follower_count == 0
1372 assert follow_info.hide_follows == true
1373 assert follow_info.following_count == 0
1374 end
1375 end
1376
1377 describe "fetch_favourites/3" do
1378 test "returns a favourite activities sorted by adds to favorite" do
1379 user = insert(:user)
1380 other_user = insert(:user)
1381 user1 = insert(:user)
1382 user2 = insert(:user)
1383 {:ok, a1} = CommonAPI.post(user1, %{status: "bla"})
1384 {:ok, _a2} = CommonAPI.post(user2, %{status: "traps are happy"})
1385 {:ok, a3} = CommonAPI.post(user2, %{status: "Trees Are "})
1386 {:ok, a4} = CommonAPI.post(user2, %{status: "Agent Smith "})
1387 {:ok, a5} = CommonAPI.post(user1, %{status: "Red or Blue "})
1388
1389 {:ok, _} = CommonAPI.favorite(user, a4.id)
1390 {:ok, _} = CommonAPI.favorite(other_user, a3.id)
1391 {:ok, _} = CommonAPI.favorite(user, a3.id)
1392 {:ok, _} = CommonAPI.favorite(other_user, a5.id)
1393 {:ok, _} = CommonAPI.favorite(user, a5.id)
1394 {:ok, _} = CommonAPI.favorite(other_user, a4.id)
1395 {:ok, _} = CommonAPI.favorite(user, a1.id)
1396 {:ok, _} = CommonAPI.favorite(other_user, a1.id)
1397 result = ActivityPub.fetch_favourites(user)
1398
1399 assert Enum.map(result, & &1.id) == [a1.id, a5.id, a3.id, a4.id]
1400
1401 result = ActivityPub.fetch_favourites(user, %{limit: 2})
1402 assert Enum.map(result, & &1.id) == [a1.id, a5.id]
1403 end
1404 end
1405
1406 describe "Move activity" do
1407 test "create" do
1408 %{ap_id: old_ap_id} = old_user = insert(:user)
1409 %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
1410 follower = insert(:user)
1411 follower_move_opted_out = insert(:user, allow_following_move: false)
1412
1413 User.follow(follower, old_user)
1414 User.follow(follower_move_opted_out, old_user)
1415
1416 assert User.following?(follower, old_user)
1417 assert User.following?(follower_move_opted_out, old_user)
1418
1419 assert {:ok, activity} = ActivityPub.move(old_user, new_user)
1420
1421 assert %Activity{
1422 actor: ^old_ap_id,
1423 data: %{
1424 "actor" => ^old_ap_id,
1425 "object" => ^old_ap_id,
1426 "target" => ^new_ap_id,
1427 "type" => "Move"
1428 },
1429 local: true
1430 } = activity
1431
1432 params = %{
1433 "op" => "move_following",
1434 "origin_id" => old_user.id,
1435 "target_id" => new_user.id
1436 }
1437
1438 assert_enqueued(worker: Pleroma.Workers.BackgroundWorker, args: params)
1439
1440 Pleroma.Workers.BackgroundWorker.perform(params, nil)
1441
1442 refute User.following?(follower, old_user)
1443 assert User.following?(follower, new_user)
1444
1445 assert User.following?(follower_move_opted_out, old_user)
1446 refute User.following?(follower_move_opted_out, new_user)
1447
1448 activity = %Activity{activity | object: nil}
1449
1450 assert [%Notification{activity: ^activity}] = Notification.for_user(follower)
1451
1452 assert [%Notification{activity: ^activity}] = Notification.for_user(follower_move_opted_out)
1453 end
1454
1455 test "old user must be in the new user's `also_known_as` list" do
1456 old_user = insert(:user)
1457 new_user = insert(:user)
1458
1459 assert {:error, "Target account must have the origin in `alsoKnownAs`"} =
1460 ActivityPub.move(old_user, new_user)
1461 end
1462 end
1463
1464 test "doesn't retrieve replies activities with exclude_replies" do
1465 user = insert(:user)
1466
1467 {:ok, activity} = CommonAPI.post(user, %{status: "yeah"})
1468
1469 {:ok, _reply} = CommonAPI.post(user, %{status: "yeah", in_reply_to_status_id: activity.id})
1470
1471 [result] = ActivityPub.fetch_public_activities(%{exclude_replies: true})
1472
1473 assert result.id == activity.id
1474
1475 assert length(ActivityPub.fetch_public_activities()) == 2
1476 end
1477
1478 describe "replies filtering with public messages" do
1479 setup :public_messages
1480
1481 test "public timeline", %{users: %{u1: user}} do
1482 activities_ids =
1483 %{}
1484 |> Map.put(:type, ["Create", "Announce"])
1485 |> Map.put(:local_only, false)
1486 |> Map.put(:blocking_user, user)
1487 |> Map.put(:muting_user, user)
1488 |> Map.put(:reply_filtering_user, user)
1489 |> ActivityPub.fetch_public_activities()
1490 |> Enum.map(& &1.id)
1491
1492 assert length(activities_ids) == 16
1493 end
1494
1495 test "public timeline with reply_visibility `following`", %{
1496 users: %{u1: user},
1497 u1: u1,
1498 u2: u2,
1499 u3: u3,
1500 u4: u4,
1501 activities: activities
1502 } do
1503 activities_ids =
1504 %{}
1505 |> Map.put(:type, ["Create", "Announce"])
1506 |> Map.put(:local_only, false)
1507 |> Map.put(:blocking_user, user)
1508 |> Map.put(:muting_user, user)
1509 |> Map.put(:reply_visibility, "following")
1510 |> Map.put(:reply_filtering_user, user)
1511 |> ActivityPub.fetch_public_activities()
1512 |> Enum.map(& &1.id)
1513
1514 assert length(activities_ids) == 14
1515
1516 visible_ids =
1517 Map.values(u1) ++ Map.values(u2) ++ Map.values(u4) ++ Map.values(activities) ++ [u3[:r1]]
1518
1519 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1520 end
1521
1522 test "public timeline with reply_visibility `self`", %{
1523 users: %{u1: user},
1524 u1: u1,
1525 u2: u2,
1526 u3: u3,
1527 u4: u4,
1528 activities: activities
1529 } do
1530 activities_ids =
1531 %{}
1532 |> Map.put(:type, ["Create", "Announce"])
1533 |> Map.put(:local_only, false)
1534 |> Map.put(:blocking_user, user)
1535 |> Map.put(:muting_user, user)
1536 |> Map.put(:reply_visibility, "self")
1537 |> Map.put(:reply_filtering_user, user)
1538 |> ActivityPub.fetch_public_activities()
1539 |> Enum.map(& &1.id)
1540
1541 assert length(activities_ids) == 10
1542 visible_ids = Map.values(u1) ++ [u2[:r1], u3[:r1], u4[:r1]] ++ Map.values(activities)
1543 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1544 end
1545
1546 test "home timeline", %{
1547 users: %{u1: user},
1548 activities: activities,
1549 u1: u1,
1550 u2: u2,
1551 u3: u3,
1552 u4: u4
1553 } do
1554 params =
1555 %{}
1556 |> Map.put(:type, ["Create", "Announce"])
1557 |> Map.put(:blocking_user, user)
1558 |> Map.put(:muting_user, user)
1559 |> Map.put(:user, user)
1560 |> Map.put(:reply_filtering_user, user)
1561
1562 activities_ids =
1563 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1564 |> Enum.map(& &1.id)
1565
1566 assert length(activities_ids) == 13
1567
1568 visible_ids =
1569 Map.values(u1) ++
1570 Map.values(u3) ++
1571 [
1572 activities[:a1],
1573 activities[:a2],
1574 activities[:a4],
1575 u2[:r1],
1576 u2[:r3],
1577 u4[:r1],
1578 u4[:r2]
1579 ]
1580
1581 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1582 end
1583
1584 test "home timeline with reply_visibility `following`", %{
1585 users: %{u1: user},
1586 activities: activities,
1587 u1: u1,
1588 u2: u2,
1589 u3: u3,
1590 u4: u4
1591 } do
1592 params =
1593 %{}
1594 |> Map.put(:type, ["Create", "Announce"])
1595 |> Map.put(:blocking_user, user)
1596 |> Map.put(:muting_user, user)
1597 |> Map.put(:user, user)
1598 |> Map.put(:reply_visibility, "following")
1599 |> Map.put(:reply_filtering_user, user)
1600
1601 activities_ids =
1602 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1603 |> Enum.map(& &1.id)
1604
1605 assert length(activities_ids) == 11
1606
1607 visible_ids =
1608 Map.values(u1) ++
1609 [
1610 activities[:a1],
1611 activities[:a2],
1612 activities[:a4],
1613 u2[:r1],
1614 u2[:r3],
1615 u3[:r1],
1616 u4[:r1],
1617 u4[:r2]
1618 ]
1619
1620 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1621 end
1622
1623 test "home timeline with reply_visibility `self`", %{
1624 users: %{u1: user},
1625 activities: activities,
1626 u1: u1,
1627 u2: u2,
1628 u3: u3,
1629 u4: u4
1630 } do
1631 params =
1632 %{}
1633 |> Map.put(:type, ["Create", "Announce"])
1634 |> Map.put(:blocking_user, user)
1635 |> Map.put(:muting_user, user)
1636 |> Map.put(:user, user)
1637 |> Map.put(:reply_visibility, "self")
1638 |> Map.put(:reply_filtering_user, user)
1639
1640 activities_ids =
1641 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1642 |> Enum.map(& &1.id)
1643
1644 assert length(activities_ids) == 9
1645
1646 visible_ids =
1647 Map.values(u1) ++
1648 [
1649 activities[:a1],
1650 activities[:a2],
1651 activities[:a4],
1652 u2[:r1],
1653 u3[:r1],
1654 u4[:r1]
1655 ]
1656
1657 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1658 end
1659
1660 test "filtering out announces where the user is the actor of the announced message" do
1661 user = insert(:user)
1662 other_user = insert(:user)
1663 third_user = insert(:user)
1664 User.follow(user, other_user)
1665
1666 {:ok, post} = CommonAPI.post(user, %{status: "yo"})
1667 {:ok, other_post} = CommonAPI.post(third_user, %{status: "yo"})
1668 {:ok, _announce} = CommonAPI.repeat(post.id, other_user)
1669 {:ok, _announce} = CommonAPI.repeat(post.id, third_user)
1670 {:ok, announce} = CommonAPI.repeat(other_post.id, other_user)
1671
1672 params = %{
1673 type: ["Announce"]
1674 }
1675
1676 results =
1677 [user.ap_id | User.following(user)]
1678 |> ActivityPub.fetch_activities(params)
1679
1680 assert length(results) == 3
1681
1682 params = %{
1683 type: ["Announce"],
1684 announce_filtering_user: user
1685 }
1686
1687 [result] =
1688 [user.ap_id | User.following(user)]
1689 |> ActivityPub.fetch_activities(params)
1690
1691 assert result.id == announce.id
1692 end
1693 end
1694
1695 describe "replies filtering with private messages" do
1696 setup :private_messages
1697
1698 test "public timeline", %{users: %{u1: user}} do
1699 activities_ids =
1700 %{}
1701 |> Map.put(:type, ["Create", "Announce"])
1702 |> Map.put(:local_only, false)
1703 |> Map.put(:blocking_user, user)
1704 |> Map.put(:muting_user, user)
1705 |> Map.put(:user, user)
1706 |> ActivityPub.fetch_public_activities()
1707 |> Enum.map(& &1.id)
1708
1709 assert activities_ids == []
1710 end
1711
1712 test "public timeline with default reply_visibility `following`", %{users: %{u1: user}} do
1713 activities_ids =
1714 %{}
1715 |> Map.put(:type, ["Create", "Announce"])
1716 |> Map.put(:local_only, false)
1717 |> Map.put(:blocking_user, user)
1718 |> Map.put(:muting_user, user)
1719 |> Map.put(:reply_visibility, "following")
1720 |> Map.put(:reply_filtering_user, user)
1721 |> Map.put(:user, user)
1722 |> ActivityPub.fetch_public_activities()
1723 |> Enum.map(& &1.id)
1724
1725 assert activities_ids == []
1726 end
1727
1728 test "public timeline with default reply_visibility `self`", %{users: %{u1: user}} do
1729 activities_ids =
1730 %{}
1731 |> Map.put(:type, ["Create", "Announce"])
1732 |> Map.put(:local_only, false)
1733 |> Map.put(:blocking_user, user)
1734 |> Map.put(:muting_user, user)
1735 |> Map.put(:reply_visibility, "self")
1736 |> Map.put(:reply_filtering_user, user)
1737 |> Map.put(:user, user)
1738 |> ActivityPub.fetch_public_activities()
1739 |> Enum.map(& &1.id)
1740
1741 assert activities_ids == []
1742 end
1743
1744 test "home timeline", %{users: %{u1: user}} do
1745 params =
1746 %{}
1747 |> Map.put(:type, ["Create", "Announce"])
1748 |> Map.put(:blocking_user, user)
1749 |> Map.put(:muting_user, user)
1750 |> Map.put(:user, user)
1751
1752 activities_ids =
1753 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1754 |> Enum.map(& &1.id)
1755
1756 assert length(activities_ids) == 12
1757 end
1758
1759 test "home timeline with default reply_visibility `following`", %{users: %{u1: user}} do
1760 params =
1761 %{}
1762 |> Map.put(:type, ["Create", "Announce"])
1763 |> Map.put(:blocking_user, user)
1764 |> Map.put(:muting_user, user)
1765 |> Map.put(:user, user)
1766 |> Map.put(:reply_visibility, "following")
1767 |> Map.put(:reply_filtering_user, user)
1768
1769 activities_ids =
1770 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1771 |> Enum.map(& &1.id)
1772
1773 assert length(activities_ids) == 12
1774 end
1775
1776 test "home timeline with default reply_visibility `self`", %{
1777 users: %{u1: user},
1778 activities: activities,
1779 u1: u1,
1780 u2: u2,
1781 u3: u3,
1782 u4: u4
1783 } do
1784 params =
1785 %{}
1786 |> Map.put(:type, ["Create", "Announce"])
1787 |> Map.put(:blocking_user, user)
1788 |> Map.put(:muting_user, user)
1789 |> Map.put(:user, user)
1790 |> Map.put(:reply_visibility, "self")
1791 |> Map.put(:reply_filtering_user, user)
1792
1793 activities_ids =
1794 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1795 |> Enum.map(& &1.id)
1796
1797 assert length(activities_ids) == 10
1798
1799 visible_ids =
1800 Map.values(u1) ++ Map.values(u4) ++ [u2[:r1], u3[:r1]] ++ Map.values(activities)
1801
1802 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1803 end
1804 end
1805
1806 defp public_messages(_) do
1807 [u1, u2, u3, u4] = insert_list(4, :user)
1808 {:ok, u1} = User.follow(u1, u2)
1809 {:ok, u2} = User.follow(u2, u1)
1810 {:ok, u1} = User.follow(u1, u4)
1811 {:ok, u4} = User.follow(u4, u1)
1812
1813 {:ok, u2} = User.follow(u2, u3)
1814 {:ok, u3} = User.follow(u3, u2)
1815
1816 {:ok, a1} = CommonAPI.post(u1, %{status: "Status"})
1817
1818 {:ok, r1_1} =
1819 CommonAPI.post(u2, %{
1820 status: "@#{u1.nickname} reply from u2 to u1",
1821 in_reply_to_status_id: a1.id
1822 })
1823
1824 {:ok, r1_2} =
1825 CommonAPI.post(u3, %{
1826 status: "@#{u1.nickname} reply from u3 to u1",
1827 in_reply_to_status_id: a1.id
1828 })
1829
1830 {:ok, r1_3} =
1831 CommonAPI.post(u4, %{
1832 status: "@#{u1.nickname} reply from u4 to u1",
1833 in_reply_to_status_id: a1.id
1834 })
1835
1836 {:ok, a2} = CommonAPI.post(u2, %{status: "Status"})
1837
1838 {:ok, r2_1} =
1839 CommonAPI.post(u1, %{
1840 status: "@#{u2.nickname} reply from u1 to u2",
1841 in_reply_to_status_id: a2.id
1842 })
1843
1844 {:ok, r2_2} =
1845 CommonAPI.post(u3, %{
1846 status: "@#{u2.nickname} reply from u3 to u2",
1847 in_reply_to_status_id: a2.id
1848 })
1849
1850 {:ok, r2_3} =
1851 CommonAPI.post(u4, %{
1852 status: "@#{u2.nickname} reply from u4 to u2",
1853 in_reply_to_status_id: a2.id
1854 })
1855
1856 {:ok, a3} = CommonAPI.post(u3, %{status: "Status"})
1857
1858 {:ok, r3_1} =
1859 CommonAPI.post(u1, %{
1860 status: "@#{u3.nickname} reply from u1 to u3",
1861 in_reply_to_status_id: a3.id
1862 })
1863
1864 {:ok, r3_2} =
1865 CommonAPI.post(u2, %{
1866 status: "@#{u3.nickname} reply from u2 to u3",
1867 in_reply_to_status_id: a3.id
1868 })
1869
1870 {:ok, r3_3} =
1871 CommonAPI.post(u4, %{
1872 status: "@#{u3.nickname} reply from u4 to u3",
1873 in_reply_to_status_id: a3.id
1874 })
1875
1876 {:ok, a4} = CommonAPI.post(u4, %{status: "Status"})
1877
1878 {:ok, r4_1} =
1879 CommonAPI.post(u1, %{
1880 status: "@#{u4.nickname} reply from u1 to u4",
1881 in_reply_to_status_id: a4.id
1882 })
1883
1884 {:ok, r4_2} =
1885 CommonAPI.post(u2, %{
1886 status: "@#{u4.nickname} reply from u2 to u4",
1887 in_reply_to_status_id: a4.id
1888 })
1889
1890 {:ok, r4_3} =
1891 CommonAPI.post(u3, %{
1892 status: "@#{u4.nickname} reply from u3 to u4",
1893 in_reply_to_status_id: a4.id
1894 })
1895
1896 {:ok,
1897 users: %{u1: u1, u2: u2, u3: u3, u4: u4},
1898 activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id},
1899 u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id},
1900 u2: %{r1: r2_1.id, r2: r2_2.id, r3: r2_3.id},
1901 u3: %{r1: r3_1.id, r2: r3_2.id, r3: r3_3.id},
1902 u4: %{r1: r4_1.id, r2: r4_2.id, r3: r4_3.id}}
1903 end
1904
1905 defp private_messages(_) do
1906 [u1, u2, u3, u4] = insert_list(4, :user)
1907 {:ok, u1} = User.follow(u1, u2)
1908 {:ok, u2} = User.follow(u2, u1)
1909 {:ok, u1} = User.follow(u1, u3)
1910 {:ok, u3} = User.follow(u3, u1)
1911 {:ok, u1} = User.follow(u1, u4)
1912 {:ok, u4} = User.follow(u4, u1)
1913
1914 {:ok, u2} = User.follow(u2, u3)
1915 {:ok, u3} = User.follow(u3, u2)
1916
1917 {:ok, a1} = CommonAPI.post(u1, %{status: "Status", visibility: "private"})
1918
1919 {:ok, r1_1} =
1920 CommonAPI.post(u2, %{
1921 status: "@#{u1.nickname} reply from u2 to u1",
1922 in_reply_to_status_id: a1.id,
1923 visibility: "private"
1924 })
1925
1926 {:ok, r1_2} =
1927 CommonAPI.post(u3, %{
1928 status: "@#{u1.nickname} reply from u3 to u1",
1929 in_reply_to_status_id: a1.id,
1930 visibility: "private"
1931 })
1932
1933 {:ok, r1_3} =
1934 CommonAPI.post(u4, %{
1935 status: "@#{u1.nickname} reply from u4 to u1",
1936 in_reply_to_status_id: a1.id,
1937 visibility: "private"
1938 })
1939
1940 {:ok, a2} = CommonAPI.post(u2, %{status: "Status", visibility: "private"})
1941
1942 {:ok, r2_1} =
1943 CommonAPI.post(u1, %{
1944 status: "@#{u2.nickname} reply from u1 to u2",
1945 in_reply_to_status_id: a2.id,
1946 visibility: "private"
1947 })
1948
1949 {:ok, r2_2} =
1950 CommonAPI.post(u3, %{
1951 status: "@#{u2.nickname} reply from u3 to u2",
1952 in_reply_to_status_id: a2.id,
1953 visibility: "private"
1954 })
1955
1956 {:ok, a3} = CommonAPI.post(u3, %{status: "Status", visibility: "private"})
1957
1958 {:ok, r3_1} =
1959 CommonAPI.post(u1, %{
1960 status: "@#{u3.nickname} reply from u1 to u3",
1961 in_reply_to_status_id: a3.id,
1962 visibility: "private"
1963 })
1964
1965 {:ok, r3_2} =
1966 CommonAPI.post(u2, %{
1967 status: "@#{u3.nickname} reply from u2 to u3",
1968 in_reply_to_status_id: a3.id,
1969 visibility: "private"
1970 })
1971
1972 {:ok, a4} = CommonAPI.post(u4, %{status: "Status", visibility: "private"})
1973
1974 {:ok, r4_1} =
1975 CommonAPI.post(u1, %{
1976 status: "@#{u4.nickname} reply from u1 to u4",
1977 in_reply_to_status_id: a4.id,
1978 visibility: "private"
1979 })
1980
1981 {:ok,
1982 users: %{u1: u1, u2: u2, u3: u3, u4: u4},
1983 activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id},
1984 u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id},
1985 u2: %{r1: r2_1.id, r2: r2_2.id},
1986 u3: %{r1: r3_1.id, r2: r3_2.id},
1987 u4: %{r1: r4_1.id}}
1988 end
1989
1990 describe "maybe_update_follow_information/1" do
1991 setup do
1992 clear_config([:instance, :external_user_synchronization], true)
1993
1994 user = %{
1995 local: false,
1996 ap_id: "https://gensokyo.2hu/users/raymoo",
1997 following_address: "https://gensokyo.2hu/users/following",
1998 follower_address: "https://gensokyo.2hu/users/followers",
1999 type: "Person"
2000 }
2001
2002 %{user: user}
2003 end
2004
2005 test "logs an error when it can't fetch the info", %{user: user} do
2006 assert capture_log(fn ->
2007 ActivityPub.maybe_update_follow_information(user)
2008 end) =~ "Follower/Following counter update for #{user.ap_id} failed"
2009 end
2010
2011 test "just returns the input if the user type is Application", %{
2012 user: user
2013 } do
2014 user =
2015 user
2016 |> Map.put(:type, "Application")
2017
2018 refute capture_log(fn ->
2019 assert ^user = ActivityPub.maybe_update_follow_information(user)
2020 end) =~ "Follower/Following counter update for #{user.ap_id} failed"
2021 end
2022
2023 test "it just returns the input if the user has no following/follower addresses", %{
2024 user: user
2025 } do
2026 user =
2027 user
2028 |> Map.put(:following_address, nil)
2029 |> Map.put(:follower_address, nil)
2030
2031 refute capture_log(fn ->
2032 assert ^user = ActivityPub.maybe_update_follow_information(user)
2033 end) =~ "Follower/Following counter update for #{user.ap_id} failed"
2034 end
2035 end
2036
2037 describe "global activity expiration" do
2038 setup do: clear_config([:mrf, :policies])
2039
2040 test "creates an activity expiration for local Create activities" do
2041 Pleroma.Config.put(
2042 [:mrf, :policies],
2043 Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy
2044 )
2045
2046 {:ok, %{id: id_create}} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"})
2047 {:ok, _follow} = ActivityBuilder.insert(%{"type" => "Follow", "context" => "3hu"})
2048
2049 assert [%{activity_id: ^id_create}] = Pleroma.ActivityExpiration |> Repo.all()
2050 end
2051 end
2052 end