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