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