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