575e0c5db0b6b19b3acebc3d85500b6b365c00b5
[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 "timeline post-processing" do
996 test "it filters broken threads" do
997 user1 = insert(:user)
998 user2 = insert(:user)
999 user3 = insert(:user)
1000
1001 {:ok, user1} = User.follow(user1, user3)
1002 assert User.following?(user1, user3)
1003
1004 {:ok, user2} = User.follow(user2, user3)
1005 assert User.following?(user2, user3)
1006
1007 {:ok, user3} = User.follow(user3, user2)
1008 assert User.following?(user3, user2)
1009
1010 {:ok, public_activity} = CommonAPI.post(user3, %{status: "hi 1"})
1011
1012 {:ok, private_activity_1} = CommonAPI.post(user3, %{status: "hi 2", visibility: "private"})
1013
1014 {:ok, private_activity_2} =
1015 CommonAPI.post(user2, %{
1016 status: "hi 3",
1017 visibility: "private",
1018 in_reply_to_status_id: private_activity_1.id
1019 })
1020
1021 {:ok, private_activity_3} =
1022 CommonAPI.post(user3, %{
1023 status: "hi 4",
1024 visibility: "private",
1025 in_reply_to_status_id: private_activity_2.id
1026 })
1027
1028 activities =
1029 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)])
1030 |> Enum.map(fn a -> a.id end)
1031
1032 private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"])
1033
1034 assert [public_activity.id, private_activity_1.id, private_activity_3.id] == activities
1035
1036 assert length(activities) == 3
1037
1038 activities =
1039 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)], %{user: user1})
1040 |> Enum.map(fn a -> a.id end)
1041
1042 assert [public_activity.id, private_activity_1.id] == activities
1043 assert length(activities) == 2
1044 end
1045 end
1046
1047 describe "flag/1" do
1048 setup do
1049 reporter = insert(:user)
1050 target_account = insert(:user)
1051 content = "foobar"
1052 {:ok, activity} = CommonAPI.post(target_account, %{status: content})
1053 context = Utils.generate_context_id()
1054
1055 reporter_ap_id = reporter.ap_id
1056 target_ap_id = target_account.ap_id
1057 activity_ap_id = activity.data["id"]
1058
1059 activity_with_object = Activity.get_by_ap_id_with_object(activity_ap_id)
1060
1061 {:ok,
1062 %{
1063 reporter: reporter,
1064 context: context,
1065 target_account: target_account,
1066 reported_activity: activity,
1067 content: content,
1068 activity_ap_id: activity_ap_id,
1069 activity_with_object: activity_with_object,
1070 reporter_ap_id: reporter_ap_id,
1071 target_ap_id: target_ap_id
1072 }}
1073 end
1074
1075 test "it can create a Flag activity",
1076 %{
1077 reporter: reporter,
1078 context: context,
1079 target_account: target_account,
1080 reported_activity: reported_activity,
1081 content: content,
1082 activity_ap_id: activity_ap_id,
1083 activity_with_object: activity_with_object,
1084 reporter_ap_id: reporter_ap_id,
1085 target_ap_id: target_ap_id
1086 } do
1087 assert {:ok, activity} =
1088 ActivityPub.flag(%{
1089 actor: reporter,
1090 context: context,
1091 account: target_account,
1092 statuses: [reported_activity],
1093 content: content
1094 })
1095
1096 note_obj = %{
1097 "type" => "Note",
1098 "id" => activity_ap_id,
1099 "content" => content,
1100 "published" => activity_with_object.object.data["published"],
1101 "actor" => AccountView.render("show.json", %{user: target_account})
1102 }
1103
1104 assert %Activity{
1105 actor: ^reporter_ap_id,
1106 data: %{
1107 "type" => "Flag",
1108 "content" => ^content,
1109 "context" => ^context,
1110 "object" => [^target_ap_id, ^note_obj]
1111 }
1112 } = activity
1113 end
1114
1115 test_with_mock "strips status data from Flag, before federating it",
1116 %{
1117 reporter: reporter,
1118 context: context,
1119 target_account: target_account,
1120 reported_activity: reported_activity,
1121 content: content
1122 },
1123 Utils,
1124 [:passthrough],
1125 [] do
1126 {:ok, activity} =
1127 ActivityPub.flag(%{
1128 actor: reporter,
1129 context: context,
1130 account: target_account,
1131 statuses: [reported_activity],
1132 content: content
1133 })
1134
1135 new_data =
1136 put_in(activity.data, ["object"], [target_account.ap_id, reported_activity.data["id"]])
1137
1138 assert_called(Utils.maybe_federate(%{activity | data: new_data}))
1139 end
1140 end
1141
1142 test "fetch_activities/2 returns activities addressed to a list " do
1143 user = insert(:user)
1144 member = insert(:user)
1145 {:ok, list} = Pleroma.List.create("foo", user)
1146 {:ok, list} = Pleroma.List.follow(list, member)
1147
1148 {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
1149
1150 activity = Repo.preload(activity, :bookmark)
1151 activity = %Activity{activity | thread_muted?: !!activity.thread_muted?}
1152
1153 assert ActivityPub.fetch_activities([], %{user: user}) == [activity]
1154 end
1155
1156 def data_uri do
1157 File.read!("test/fixtures/avatar_data_uri")
1158 end
1159
1160 describe "fetch_activities_bounded" do
1161 test "fetches private posts for followed users" do
1162 user = insert(:user)
1163
1164 {:ok, activity} =
1165 CommonAPI.post(user, %{
1166 status: "thought I looked cute might delete later :3",
1167 visibility: "private"
1168 })
1169
1170 [result] = ActivityPub.fetch_activities_bounded([user.follower_address], [])
1171 assert result.id == activity.id
1172 end
1173
1174 test "fetches only public posts for other users" do
1175 user = insert(:user)
1176 {:ok, activity} = CommonAPI.post(user, %{status: "#cofe", visibility: "public"})
1177
1178 {:ok, _private_activity} =
1179 CommonAPI.post(user, %{
1180 status: "why is tenshi eating a corndog so cute?",
1181 visibility: "private"
1182 })
1183
1184 [result] = ActivityPub.fetch_activities_bounded([], [user.follower_address])
1185 assert result.id == activity.id
1186 end
1187 end
1188
1189 describe "fetch_follow_information_for_user" do
1190 test "syncronizes following/followers counters" do
1191 user =
1192 insert(:user,
1193 local: false,
1194 follower_address: "http://localhost:4001/users/fuser2/followers",
1195 following_address: "http://localhost:4001/users/fuser2/following"
1196 )
1197
1198 {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
1199 assert info.follower_count == 527
1200 assert info.following_count == 267
1201 end
1202
1203 test "detects hidden followers" do
1204 mock(fn env ->
1205 case env.url do
1206 "http://localhost:4001/users/masto_closed/followers?page=1" ->
1207 %Tesla.Env{status: 403, body: ""}
1208
1209 _ ->
1210 apply(HttpRequestMock, :request, [env])
1211 end
1212 end)
1213
1214 user =
1215 insert(:user,
1216 local: false,
1217 follower_address: "http://localhost:4001/users/masto_closed/followers",
1218 following_address: "http://localhost:4001/users/masto_closed/following"
1219 )
1220
1221 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1222 assert follow_info.hide_followers == true
1223 assert follow_info.hide_follows == false
1224 end
1225
1226 test "detects hidden follows" do
1227 mock(fn env ->
1228 case env.url do
1229 "http://localhost:4001/users/masto_closed/following?page=1" ->
1230 %Tesla.Env{status: 403, body: ""}
1231
1232 _ ->
1233 apply(HttpRequestMock, :request, [env])
1234 end
1235 end)
1236
1237 user =
1238 insert(:user,
1239 local: false,
1240 follower_address: "http://localhost:4001/users/masto_closed/followers",
1241 following_address: "http://localhost:4001/users/masto_closed/following"
1242 )
1243
1244 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1245 assert follow_info.hide_followers == false
1246 assert follow_info.hide_follows == true
1247 end
1248
1249 test "detects hidden follows/followers for friendica" do
1250 user =
1251 insert(:user,
1252 local: false,
1253 follower_address: "http://localhost:8080/followers/fuser3",
1254 following_address: "http://localhost:8080/following/fuser3"
1255 )
1256
1257 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1258 assert follow_info.hide_followers == true
1259 assert follow_info.follower_count == 296
1260 assert follow_info.following_count == 32
1261 assert follow_info.hide_follows == true
1262 end
1263
1264 test "doesn't crash when follower and following counters are hidden" do
1265 mock(fn env ->
1266 case env.url do
1267 "http://localhost:4001/users/masto_hidden_counters/following" ->
1268 json(%{
1269 "@context" => "https://www.w3.org/ns/activitystreams",
1270 "id" => "http://localhost:4001/users/masto_hidden_counters/followers"
1271 })
1272
1273 "http://localhost:4001/users/masto_hidden_counters/following?page=1" ->
1274 %Tesla.Env{status: 403, body: ""}
1275
1276 "http://localhost:4001/users/masto_hidden_counters/followers" ->
1277 json(%{
1278 "@context" => "https://www.w3.org/ns/activitystreams",
1279 "id" => "http://localhost:4001/users/masto_hidden_counters/following"
1280 })
1281
1282 "http://localhost:4001/users/masto_hidden_counters/followers?page=1" ->
1283 %Tesla.Env{status: 403, body: ""}
1284 end
1285 end)
1286
1287 user =
1288 insert(:user,
1289 local: false,
1290 follower_address: "http://localhost:4001/users/masto_hidden_counters/followers",
1291 following_address: "http://localhost:4001/users/masto_hidden_counters/following"
1292 )
1293
1294 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1295
1296 assert follow_info.hide_followers == true
1297 assert follow_info.follower_count == 0
1298 assert follow_info.hide_follows == true
1299 assert follow_info.following_count == 0
1300 end
1301 end
1302
1303 describe "fetch_favourites/3" do
1304 test "returns a favourite activities sorted by adds to favorite" do
1305 user = insert(:user)
1306 other_user = insert(:user)
1307 user1 = insert(:user)
1308 user2 = insert(:user)
1309 {:ok, a1} = CommonAPI.post(user1, %{status: "bla"})
1310 {:ok, _a2} = CommonAPI.post(user2, %{status: "traps are happy"})
1311 {:ok, a3} = CommonAPI.post(user2, %{status: "Trees Are "})
1312 {:ok, a4} = CommonAPI.post(user2, %{status: "Agent Smith "})
1313 {:ok, a5} = CommonAPI.post(user1, %{status: "Red or Blue "})
1314
1315 {:ok, _} = CommonAPI.favorite(user, a4.id)
1316 {:ok, _} = CommonAPI.favorite(other_user, a3.id)
1317 {:ok, _} = CommonAPI.favorite(user, a3.id)
1318 {:ok, _} = CommonAPI.favorite(other_user, a5.id)
1319 {:ok, _} = CommonAPI.favorite(user, a5.id)
1320 {:ok, _} = CommonAPI.favorite(other_user, a4.id)
1321 {:ok, _} = CommonAPI.favorite(user, a1.id)
1322 {:ok, _} = CommonAPI.favorite(other_user, a1.id)
1323 result = ActivityPub.fetch_favourites(user)
1324
1325 assert Enum.map(result, & &1.id) == [a1.id, a5.id, a3.id, a4.id]
1326
1327 result = ActivityPub.fetch_favourites(user, %{limit: 2})
1328 assert Enum.map(result, & &1.id) == [a1.id, a5.id]
1329 end
1330 end
1331
1332 describe "Move activity" do
1333 test "create" do
1334 %{ap_id: old_ap_id} = old_user = insert(:user)
1335 %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
1336 follower = insert(:user)
1337 follower_move_opted_out = insert(:user, allow_following_move: false)
1338
1339 User.follow(follower, old_user)
1340 User.follow(follower_move_opted_out, old_user)
1341
1342 assert User.following?(follower, old_user)
1343 assert User.following?(follower_move_opted_out, old_user)
1344
1345 assert {:ok, activity} = ActivityPub.move(old_user, new_user)
1346
1347 assert %Activity{
1348 actor: ^old_ap_id,
1349 data: %{
1350 "actor" => ^old_ap_id,
1351 "object" => ^old_ap_id,
1352 "target" => ^new_ap_id,
1353 "type" => "Move"
1354 },
1355 local: true
1356 } = activity
1357
1358 params = %{
1359 "op" => "move_following",
1360 "origin_id" => old_user.id,
1361 "target_id" => new_user.id
1362 }
1363
1364 assert_enqueued(worker: Pleroma.Workers.BackgroundWorker, args: params)
1365
1366 Pleroma.Workers.BackgroundWorker.perform(params, nil)
1367
1368 refute User.following?(follower, old_user)
1369 assert User.following?(follower, new_user)
1370
1371 assert User.following?(follower_move_opted_out, old_user)
1372 refute User.following?(follower_move_opted_out, new_user)
1373
1374 activity = %Activity{activity | object: nil}
1375
1376 assert [%Notification{activity: ^activity}] = Notification.for_user(follower)
1377
1378 assert [%Notification{activity: ^activity}] = Notification.for_user(follower_move_opted_out)
1379 end
1380
1381 test "old user must be in the new user's `also_known_as` list" do
1382 old_user = insert(:user)
1383 new_user = insert(:user)
1384
1385 assert {:error, "Target account must have the origin in `alsoKnownAs`"} =
1386 ActivityPub.move(old_user, new_user)
1387 end
1388 end
1389
1390 test "doesn't retrieve replies activities with exclude_replies" do
1391 user = insert(:user)
1392
1393 {:ok, activity} = CommonAPI.post(user, %{status: "yeah"})
1394
1395 {:ok, _reply} = CommonAPI.post(user, %{status: "yeah", in_reply_to_status_id: activity.id})
1396
1397 [result] = ActivityPub.fetch_public_activities(%{exclude_replies: true})
1398
1399 assert result.id == activity.id
1400
1401 assert length(ActivityPub.fetch_public_activities()) == 2
1402 end
1403
1404 describe "replies filtering with public messages" do
1405 setup :public_messages
1406
1407 test "public timeline", %{users: %{u1: user}} do
1408 activities_ids =
1409 %{}
1410 |> Map.put(:type, ["Create", "Announce"])
1411 |> Map.put(:local_only, false)
1412 |> Map.put(:blocking_user, user)
1413 |> Map.put(:muting_user, user)
1414 |> Map.put(:reply_filtering_user, user)
1415 |> ActivityPub.fetch_public_activities()
1416 |> Enum.map(& &1.id)
1417
1418 assert length(activities_ids) == 16
1419 end
1420
1421 test "public timeline with reply_visibility `following`", %{
1422 users: %{u1: user},
1423 u1: u1,
1424 u2: u2,
1425 u3: u3,
1426 u4: u4,
1427 activities: activities
1428 } do
1429 activities_ids =
1430 %{}
1431 |> Map.put(:type, ["Create", "Announce"])
1432 |> Map.put(:local_only, false)
1433 |> Map.put(:blocking_user, user)
1434 |> Map.put(:muting_user, user)
1435 |> Map.put(:reply_visibility, "following")
1436 |> Map.put(:reply_filtering_user, user)
1437 |> ActivityPub.fetch_public_activities()
1438 |> Enum.map(& &1.id)
1439
1440 assert length(activities_ids) == 14
1441
1442 visible_ids =
1443 Map.values(u1) ++ Map.values(u2) ++ Map.values(u4) ++ Map.values(activities) ++ [u3[:r1]]
1444
1445 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1446 end
1447
1448 test "public timeline with reply_visibility `self`", %{
1449 users: %{u1: user},
1450 u1: u1,
1451 u2: u2,
1452 u3: u3,
1453 u4: u4,
1454 activities: activities
1455 } do
1456 activities_ids =
1457 %{}
1458 |> Map.put(:type, ["Create", "Announce"])
1459 |> Map.put(:local_only, false)
1460 |> Map.put(:blocking_user, user)
1461 |> Map.put(:muting_user, user)
1462 |> Map.put(:reply_visibility, "self")
1463 |> Map.put(:reply_filtering_user, user)
1464 |> ActivityPub.fetch_public_activities()
1465 |> Enum.map(& &1.id)
1466
1467 assert length(activities_ids) == 10
1468 visible_ids = Map.values(u1) ++ [u2[:r1], u3[:r1], u4[:r1]] ++ Map.values(activities)
1469 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1470 end
1471
1472 test "home timeline", %{
1473 users: %{u1: user},
1474 activities: activities,
1475 u1: u1,
1476 u2: u2,
1477 u3: u3,
1478 u4: u4
1479 } do
1480 params =
1481 %{}
1482 |> Map.put(:type, ["Create", "Announce"])
1483 |> Map.put(:blocking_user, user)
1484 |> Map.put(:muting_user, user)
1485 |> Map.put(:user, user)
1486 |> Map.put(:reply_filtering_user, user)
1487
1488 activities_ids =
1489 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1490 |> Enum.map(& &1.id)
1491
1492 assert length(activities_ids) == 13
1493
1494 visible_ids =
1495 Map.values(u1) ++
1496 Map.values(u3) ++
1497 [
1498 activities[:a1],
1499 activities[:a2],
1500 activities[:a4],
1501 u2[:r1],
1502 u2[:r3],
1503 u4[:r1],
1504 u4[:r2]
1505 ]
1506
1507 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1508 end
1509
1510 test "home timeline with reply_visibility `following`", %{
1511 users: %{u1: user},
1512 activities: activities,
1513 u1: u1,
1514 u2: u2,
1515 u3: u3,
1516 u4: u4
1517 } do
1518 params =
1519 %{}
1520 |> Map.put(:type, ["Create", "Announce"])
1521 |> Map.put(:blocking_user, user)
1522 |> Map.put(:muting_user, user)
1523 |> Map.put(:user, user)
1524 |> Map.put(:reply_visibility, "following")
1525 |> Map.put(:reply_filtering_user, user)
1526
1527 activities_ids =
1528 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1529 |> Enum.map(& &1.id)
1530
1531 assert length(activities_ids) == 11
1532
1533 visible_ids =
1534 Map.values(u1) ++
1535 [
1536 activities[:a1],
1537 activities[:a2],
1538 activities[:a4],
1539 u2[:r1],
1540 u2[:r3],
1541 u3[:r1],
1542 u4[:r1],
1543 u4[:r2]
1544 ]
1545
1546 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1547 end
1548
1549 test "home timeline with reply_visibility `self`", %{
1550 users: %{u1: user},
1551 activities: activities,
1552 u1: u1,
1553 u2: u2,
1554 u3: u3,
1555 u4: u4
1556 } do
1557 params =
1558 %{}
1559 |> Map.put(:type, ["Create", "Announce"])
1560 |> Map.put(:blocking_user, user)
1561 |> Map.put(:muting_user, user)
1562 |> Map.put(:user, user)
1563 |> Map.put(:reply_visibility, "self")
1564 |> Map.put(:reply_filtering_user, user)
1565
1566 activities_ids =
1567 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1568 |> Enum.map(& &1.id)
1569
1570 assert length(activities_ids) == 9
1571
1572 visible_ids =
1573 Map.values(u1) ++
1574 [
1575 activities[:a1],
1576 activities[:a2],
1577 activities[:a4],
1578 u2[:r1],
1579 u3[:r1],
1580 u4[:r1]
1581 ]
1582
1583 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1584 end
1585
1586 test "filtering out announces where the user is the actor of the announced message" do
1587 user = insert(:user)
1588 other_user = insert(:user)
1589 third_user = insert(:user)
1590 User.follow(user, other_user)
1591
1592 {:ok, post} = CommonAPI.post(user, %{status: "yo"})
1593 {:ok, other_post} = CommonAPI.post(third_user, %{status: "yo"})
1594 {:ok, _announce} = CommonAPI.repeat(post.id, other_user)
1595 {:ok, _announce} = CommonAPI.repeat(post.id, third_user)
1596 {:ok, announce} = CommonAPI.repeat(other_post.id, other_user)
1597
1598 params = %{
1599 type: ["Announce"]
1600 }
1601
1602 results =
1603 [user.ap_id | User.following(user)]
1604 |> ActivityPub.fetch_activities(params)
1605
1606 assert length(results) == 3
1607
1608 params = %{
1609 type: ["Announce"],
1610 announce_filtering_user: user
1611 }
1612
1613 [result] =
1614 [user.ap_id | User.following(user)]
1615 |> ActivityPub.fetch_activities(params)
1616
1617 assert result.id == announce.id
1618 end
1619 end
1620
1621 describe "replies filtering with private messages" do
1622 setup :private_messages
1623
1624 test "public timeline", %{users: %{u1: user}} do
1625 activities_ids =
1626 %{}
1627 |> Map.put(:type, ["Create", "Announce"])
1628 |> Map.put(:local_only, false)
1629 |> Map.put(:blocking_user, user)
1630 |> Map.put(:muting_user, user)
1631 |> Map.put(:user, user)
1632 |> ActivityPub.fetch_public_activities()
1633 |> Enum.map(& &1.id)
1634
1635 assert activities_ids == []
1636 end
1637
1638 test "public timeline with default reply_visibility `following`", %{users: %{u1: user}} do
1639 activities_ids =
1640 %{}
1641 |> Map.put(:type, ["Create", "Announce"])
1642 |> Map.put(:local_only, false)
1643 |> Map.put(:blocking_user, user)
1644 |> Map.put(:muting_user, user)
1645 |> Map.put(:reply_visibility, "following")
1646 |> Map.put(:reply_filtering_user, user)
1647 |> Map.put(:user, user)
1648 |> ActivityPub.fetch_public_activities()
1649 |> Enum.map(& &1.id)
1650
1651 assert activities_ids == []
1652 end
1653
1654 test "public timeline with default reply_visibility `self`", %{users: %{u1: user}} do
1655 activities_ids =
1656 %{}
1657 |> Map.put(:type, ["Create", "Announce"])
1658 |> Map.put(:local_only, false)
1659 |> Map.put(:blocking_user, user)
1660 |> Map.put(:muting_user, user)
1661 |> Map.put(:reply_visibility, "self")
1662 |> Map.put(:reply_filtering_user, user)
1663 |> Map.put(:user, user)
1664 |> ActivityPub.fetch_public_activities()
1665 |> Enum.map(& &1.id)
1666
1667 assert activities_ids == []
1668 end
1669
1670 test "home timeline", %{users: %{u1: user}} do
1671 params =
1672 %{}
1673 |> Map.put(:type, ["Create", "Announce"])
1674 |> Map.put(:blocking_user, user)
1675 |> Map.put(:muting_user, user)
1676 |> Map.put(:user, user)
1677
1678 activities_ids =
1679 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1680 |> Enum.map(& &1.id)
1681
1682 assert length(activities_ids) == 12
1683 end
1684
1685 test "home timeline with default reply_visibility `following`", %{users: %{u1: user}} do
1686 params =
1687 %{}
1688 |> Map.put(:type, ["Create", "Announce"])
1689 |> Map.put(:blocking_user, user)
1690 |> Map.put(:muting_user, user)
1691 |> Map.put(:user, user)
1692 |> Map.put(:reply_visibility, "following")
1693 |> Map.put(:reply_filtering_user, user)
1694
1695 activities_ids =
1696 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1697 |> Enum.map(& &1.id)
1698
1699 assert length(activities_ids) == 12
1700 end
1701
1702 test "home timeline with default reply_visibility `self`", %{
1703 users: %{u1: user},
1704 activities: activities,
1705 u1: u1,
1706 u2: u2,
1707 u3: u3,
1708 u4: u4
1709 } do
1710 params =
1711 %{}
1712 |> Map.put(:type, ["Create", "Announce"])
1713 |> Map.put(:blocking_user, user)
1714 |> Map.put(:muting_user, user)
1715 |> Map.put(:user, user)
1716 |> Map.put(:reply_visibility, "self")
1717 |> Map.put(:reply_filtering_user, user)
1718
1719 activities_ids =
1720 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1721 |> Enum.map(& &1.id)
1722
1723 assert length(activities_ids) == 10
1724
1725 visible_ids =
1726 Map.values(u1) ++ Map.values(u4) ++ [u2[:r1], u3[:r1]] ++ Map.values(activities)
1727
1728 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1729 end
1730 end
1731
1732 defp public_messages(_) do
1733 [u1, u2, u3, u4] = insert_list(4, :user)
1734 {:ok, u1} = User.follow(u1, u2)
1735 {:ok, u2} = User.follow(u2, u1)
1736 {:ok, u1} = User.follow(u1, u4)
1737 {:ok, u4} = User.follow(u4, u1)
1738
1739 {:ok, u2} = User.follow(u2, u3)
1740 {:ok, u3} = User.follow(u3, u2)
1741
1742 {:ok, a1} = CommonAPI.post(u1, %{status: "Status"})
1743
1744 {:ok, r1_1} =
1745 CommonAPI.post(u2, %{
1746 status: "@#{u1.nickname} reply from u2 to u1",
1747 in_reply_to_status_id: a1.id
1748 })
1749
1750 {:ok, r1_2} =
1751 CommonAPI.post(u3, %{
1752 status: "@#{u1.nickname} reply from u3 to u1",
1753 in_reply_to_status_id: a1.id
1754 })
1755
1756 {:ok, r1_3} =
1757 CommonAPI.post(u4, %{
1758 status: "@#{u1.nickname} reply from u4 to u1",
1759 in_reply_to_status_id: a1.id
1760 })
1761
1762 {:ok, a2} = CommonAPI.post(u2, %{status: "Status"})
1763
1764 {:ok, r2_1} =
1765 CommonAPI.post(u1, %{
1766 status: "@#{u2.nickname} reply from u1 to u2",
1767 in_reply_to_status_id: a2.id
1768 })
1769
1770 {:ok, r2_2} =
1771 CommonAPI.post(u3, %{
1772 status: "@#{u2.nickname} reply from u3 to u2",
1773 in_reply_to_status_id: a2.id
1774 })
1775
1776 {:ok, r2_3} =
1777 CommonAPI.post(u4, %{
1778 status: "@#{u2.nickname} reply from u4 to u2",
1779 in_reply_to_status_id: a2.id
1780 })
1781
1782 {:ok, a3} = CommonAPI.post(u3, %{status: "Status"})
1783
1784 {:ok, r3_1} =
1785 CommonAPI.post(u1, %{
1786 status: "@#{u3.nickname} reply from u1 to u3",
1787 in_reply_to_status_id: a3.id
1788 })
1789
1790 {:ok, r3_2} =
1791 CommonAPI.post(u2, %{
1792 status: "@#{u3.nickname} reply from u2 to u3",
1793 in_reply_to_status_id: a3.id
1794 })
1795
1796 {:ok, r3_3} =
1797 CommonAPI.post(u4, %{
1798 status: "@#{u3.nickname} reply from u4 to u3",
1799 in_reply_to_status_id: a3.id
1800 })
1801
1802 {:ok, a4} = CommonAPI.post(u4, %{status: "Status"})
1803
1804 {:ok, r4_1} =
1805 CommonAPI.post(u1, %{
1806 status: "@#{u4.nickname} reply from u1 to u4",
1807 in_reply_to_status_id: a4.id
1808 })
1809
1810 {:ok, r4_2} =
1811 CommonAPI.post(u2, %{
1812 status: "@#{u4.nickname} reply from u2 to u4",
1813 in_reply_to_status_id: a4.id
1814 })
1815
1816 {:ok, r4_3} =
1817 CommonAPI.post(u3, %{
1818 status: "@#{u4.nickname} reply from u3 to u4",
1819 in_reply_to_status_id: a4.id
1820 })
1821
1822 {:ok,
1823 users: %{u1: u1, u2: u2, u3: u3, u4: u4},
1824 activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id},
1825 u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id},
1826 u2: %{r1: r2_1.id, r2: r2_2.id, r3: r2_3.id},
1827 u3: %{r1: r3_1.id, r2: r3_2.id, r3: r3_3.id},
1828 u4: %{r1: r4_1.id, r2: r4_2.id, r3: r4_3.id}}
1829 end
1830
1831 defp private_messages(_) do
1832 [u1, u2, u3, u4] = insert_list(4, :user)
1833 {:ok, u1} = User.follow(u1, u2)
1834 {:ok, u2} = User.follow(u2, u1)
1835 {:ok, u1} = User.follow(u1, u3)
1836 {:ok, u3} = User.follow(u3, u1)
1837 {:ok, u1} = User.follow(u1, u4)
1838 {:ok, u4} = User.follow(u4, u1)
1839
1840 {:ok, u2} = User.follow(u2, u3)
1841 {:ok, u3} = User.follow(u3, u2)
1842
1843 {:ok, a1} = CommonAPI.post(u1, %{status: "Status", visibility: "private"})
1844
1845 {:ok, r1_1} =
1846 CommonAPI.post(u2, %{
1847 status: "@#{u1.nickname} reply from u2 to u1",
1848 in_reply_to_status_id: a1.id,
1849 visibility: "private"
1850 })
1851
1852 {:ok, r1_2} =
1853 CommonAPI.post(u3, %{
1854 status: "@#{u1.nickname} reply from u3 to u1",
1855 in_reply_to_status_id: a1.id,
1856 visibility: "private"
1857 })
1858
1859 {:ok, r1_3} =
1860 CommonAPI.post(u4, %{
1861 status: "@#{u1.nickname} reply from u4 to u1",
1862 in_reply_to_status_id: a1.id,
1863 visibility: "private"
1864 })
1865
1866 {:ok, a2} = CommonAPI.post(u2, %{status: "Status", visibility: "private"})
1867
1868 {:ok, r2_1} =
1869 CommonAPI.post(u1, %{
1870 status: "@#{u2.nickname} reply from u1 to u2",
1871 in_reply_to_status_id: a2.id,
1872 visibility: "private"
1873 })
1874
1875 {:ok, r2_2} =
1876 CommonAPI.post(u3, %{
1877 status: "@#{u2.nickname} reply from u3 to u2",
1878 in_reply_to_status_id: a2.id,
1879 visibility: "private"
1880 })
1881
1882 {:ok, a3} = CommonAPI.post(u3, %{status: "Status", visibility: "private"})
1883
1884 {:ok, r3_1} =
1885 CommonAPI.post(u1, %{
1886 status: "@#{u3.nickname} reply from u1 to u3",
1887 in_reply_to_status_id: a3.id,
1888 visibility: "private"
1889 })
1890
1891 {:ok, r3_2} =
1892 CommonAPI.post(u2, %{
1893 status: "@#{u3.nickname} reply from u2 to u3",
1894 in_reply_to_status_id: a3.id,
1895 visibility: "private"
1896 })
1897
1898 {:ok, a4} = CommonAPI.post(u4, %{status: "Status", visibility: "private"})
1899
1900 {:ok, r4_1} =
1901 CommonAPI.post(u1, %{
1902 status: "@#{u4.nickname} reply from u1 to u4",
1903 in_reply_to_status_id: a4.id,
1904 visibility: "private"
1905 })
1906
1907 {:ok,
1908 users: %{u1: u1, u2: u2, u3: u3, u4: u4},
1909 activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id},
1910 u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id},
1911 u2: %{r1: r2_1.id, r2: r2_2.id},
1912 u3: %{r1: r3_1.id, r2: r3_2.id},
1913 u4: %{r1: r4_1.id}}
1914 end
1915
1916 describe "maybe_update_follow_information/1" do
1917 setup do
1918 clear_config([:instance, :external_user_synchronization], true)
1919
1920 user = %{
1921 local: false,
1922 ap_id: "https://gensokyo.2hu/users/raymoo",
1923 following_address: "https://gensokyo.2hu/users/following",
1924 follower_address: "https://gensokyo.2hu/users/followers",
1925 type: "Person"
1926 }
1927
1928 %{user: user}
1929 end
1930
1931 test "logs an error when it can't fetch the info", %{user: user} do
1932 assert capture_log(fn ->
1933 ActivityPub.maybe_update_follow_information(user)
1934 end) =~ "Follower/Following counter update for #{user.ap_id} failed"
1935 end
1936
1937 test "just returns the input if the user type is Application", %{
1938 user: user
1939 } do
1940 user =
1941 user
1942 |> Map.put(:type, "Application")
1943
1944 refute capture_log(fn ->
1945 assert ^user = ActivityPub.maybe_update_follow_information(user)
1946 end) =~ "Follower/Following counter update for #{user.ap_id} failed"
1947 end
1948
1949 test "it just returns the input if the user has no following/follower addresses", %{
1950 user: user
1951 } do
1952 user =
1953 user
1954 |> Map.put(:following_address, nil)
1955 |> Map.put(:follower_address, nil)
1956
1957 refute capture_log(fn ->
1958 assert ^user = ActivityPub.maybe_update_follow_information(user)
1959 end) =~ "Follower/Following counter update for #{user.ap_id} failed"
1960 end
1961 end
1962
1963 describe "global activity expiration" do
1964 setup do: clear_config([:mrf, :policies])
1965
1966 test "creates an activity expiration for local Create activities" do
1967 Pleroma.Config.put(
1968 [:mrf, :policies],
1969 Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy
1970 )
1971
1972 {:ok, %{id: id_create}} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"})
1973 {:ok, _follow} = ActivityBuilder.insert(%{"type" => "Follow", "context" => "3hu"})
1974
1975 assert [%{activity_id: ^id_create}] = Pleroma.ActivityExpiration |> Repo.all()
1976 end
1977 end
1978 end