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