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