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