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