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