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