Merge branch '1794-hashtags-construction-from-uri-fix' into 'develop'
[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 end
1647
1648 describe "replies filtering with private messages" do
1649 setup :private_messages
1650
1651 test "public timeline", %{users: %{u1: user}} do
1652 activities_ids =
1653 %{}
1654 |> Map.put(:type, ["Create", "Announce"])
1655 |> Map.put(:local_only, false)
1656 |> Map.put(:blocking_user, user)
1657 |> Map.put(:muting_user, user)
1658 |> Map.put(:user, user)
1659 |> ActivityPub.fetch_public_activities()
1660 |> Enum.map(& &1.id)
1661
1662 assert activities_ids == []
1663 end
1664
1665 test "public timeline with default reply_visibility `following`", %{users: %{u1: user}} do
1666 activities_ids =
1667 %{}
1668 |> Map.put(:type, ["Create", "Announce"])
1669 |> Map.put(:local_only, false)
1670 |> Map.put(:blocking_user, user)
1671 |> Map.put(:muting_user, user)
1672 |> Map.put(:reply_visibility, "following")
1673 |> Map.put(:reply_filtering_user, user)
1674 |> Map.put(:user, user)
1675 |> ActivityPub.fetch_public_activities()
1676 |> Enum.map(& &1.id)
1677
1678 assert activities_ids == []
1679 end
1680
1681 test "public timeline with default reply_visibility `self`", %{users: %{u1: user}} do
1682 activities_ids =
1683 %{}
1684 |> Map.put(:type, ["Create", "Announce"])
1685 |> Map.put(:local_only, false)
1686 |> Map.put(:blocking_user, user)
1687 |> Map.put(:muting_user, user)
1688 |> Map.put(:reply_visibility, "self")
1689 |> Map.put(:reply_filtering_user, user)
1690 |> Map.put(:user, user)
1691 |> ActivityPub.fetch_public_activities()
1692 |> Enum.map(& &1.id)
1693
1694 assert activities_ids == []
1695 end
1696
1697 test "home timeline", %{users: %{u1: user}} do
1698 params =
1699 %{}
1700 |> Map.put(:type, ["Create", "Announce"])
1701 |> Map.put(:blocking_user, user)
1702 |> Map.put(:muting_user, user)
1703 |> Map.put(:user, user)
1704
1705 activities_ids =
1706 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1707 |> Enum.map(& &1.id)
1708
1709 assert length(activities_ids) == 12
1710 end
1711
1712 test "home timeline with default reply_visibility `following`", %{users: %{u1: user}} do
1713 params =
1714 %{}
1715 |> Map.put(:type, ["Create", "Announce"])
1716 |> Map.put(:blocking_user, user)
1717 |> Map.put(:muting_user, user)
1718 |> Map.put(:user, user)
1719 |> Map.put(:reply_visibility, "following")
1720 |> Map.put(:reply_filtering_user, user)
1721
1722 activities_ids =
1723 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1724 |> Enum.map(& &1.id)
1725
1726 assert length(activities_ids) == 12
1727 end
1728
1729 test "home timeline with default reply_visibility `self`", %{
1730 users: %{u1: user},
1731 activities: activities,
1732 u1: u1,
1733 u2: u2,
1734 u3: u3,
1735 u4: u4
1736 } do
1737 params =
1738 %{}
1739 |> Map.put(:type, ["Create", "Announce"])
1740 |> Map.put(:blocking_user, user)
1741 |> Map.put(:muting_user, user)
1742 |> Map.put(:user, user)
1743 |> Map.put(:reply_visibility, "self")
1744 |> Map.put(:reply_filtering_user, user)
1745
1746 activities_ids =
1747 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1748 |> Enum.map(& &1.id)
1749
1750 assert length(activities_ids) == 10
1751
1752 visible_ids =
1753 Map.values(u1) ++ Map.values(u4) ++ [u2[:r1], u3[:r1]] ++ Map.values(activities)
1754
1755 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1756 end
1757 end
1758
1759 defp public_messages(_) do
1760 [u1, u2, u3, u4] = insert_list(4, :user)
1761 {:ok, u1} = User.follow(u1, u2)
1762 {:ok, u2} = User.follow(u2, u1)
1763 {:ok, u1} = User.follow(u1, u4)
1764 {:ok, u4} = User.follow(u4, u1)
1765
1766 {:ok, u2} = User.follow(u2, u3)
1767 {:ok, u3} = User.follow(u3, u2)
1768
1769 {:ok, a1} = CommonAPI.post(u1, %{status: "Status"})
1770
1771 {:ok, r1_1} =
1772 CommonAPI.post(u2, %{
1773 status: "@#{u1.nickname} reply from u2 to u1",
1774 in_reply_to_status_id: a1.id
1775 })
1776
1777 {:ok, r1_2} =
1778 CommonAPI.post(u3, %{
1779 status: "@#{u1.nickname} reply from u3 to u1",
1780 in_reply_to_status_id: a1.id
1781 })
1782
1783 {:ok, r1_3} =
1784 CommonAPI.post(u4, %{
1785 status: "@#{u1.nickname} reply from u4 to u1",
1786 in_reply_to_status_id: a1.id
1787 })
1788
1789 {:ok, a2} = CommonAPI.post(u2, %{status: "Status"})
1790
1791 {:ok, r2_1} =
1792 CommonAPI.post(u1, %{
1793 status: "@#{u2.nickname} reply from u1 to u2",
1794 in_reply_to_status_id: a2.id
1795 })
1796
1797 {:ok, r2_2} =
1798 CommonAPI.post(u3, %{
1799 status: "@#{u2.nickname} reply from u3 to u2",
1800 in_reply_to_status_id: a2.id
1801 })
1802
1803 {:ok, r2_3} =
1804 CommonAPI.post(u4, %{
1805 status: "@#{u2.nickname} reply from u4 to u2",
1806 in_reply_to_status_id: a2.id
1807 })
1808
1809 {:ok, a3} = CommonAPI.post(u3, %{status: "Status"})
1810
1811 {:ok, r3_1} =
1812 CommonAPI.post(u1, %{
1813 status: "@#{u3.nickname} reply from u1 to u3",
1814 in_reply_to_status_id: a3.id
1815 })
1816
1817 {:ok, r3_2} =
1818 CommonAPI.post(u2, %{
1819 status: "@#{u3.nickname} reply from u2 to u3",
1820 in_reply_to_status_id: a3.id
1821 })
1822
1823 {:ok, r3_3} =
1824 CommonAPI.post(u4, %{
1825 status: "@#{u3.nickname} reply from u4 to u3",
1826 in_reply_to_status_id: a3.id
1827 })
1828
1829 {:ok, a4} = CommonAPI.post(u4, %{status: "Status"})
1830
1831 {:ok, r4_1} =
1832 CommonAPI.post(u1, %{
1833 status: "@#{u4.nickname} reply from u1 to u4",
1834 in_reply_to_status_id: a4.id
1835 })
1836
1837 {:ok, r4_2} =
1838 CommonAPI.post(u2, %{
1839 status: "@#{u4.nickname} reply from u2 to u4",
1840 in_reply_to_status_id: a4.id
1841 })
1842
1843 {:ok, r4_3} =
1844 CommonAPI.post(u3, %{
1845 status: "@#{u4.nickname} reply from u3 to u4",
1846 in_reply_to_status_id: a4.id
1847 })
1848
1849 {:ok,
1850 users: %{u1: u1, u2: u2, u3: u3, u4: u4},
1851 activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id},
1852 u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id},
1853 u2: %{r1: r2_1.id, r2: r2_2.id, r3: r2_3.id},
1854 u3: %{r1: r3_1.id, r2: r3_2.id, r3: r3_3.id},
1855 u4: %{r1: r4_1.id, r2: r4_2.id, r3: r4_3.id}}
1856 end
1857
1858 defp private_messages(_) do
1859 [u1, u2, u3, u4] = insert_list(4, :user)
1860 {:ok, u1} = User.follow(u1, u2)
1861 {:ok, u2} = User.follow(u2, u1)
1862 {:ok, u1} = User.follow(u1, u3)
1863 {:ok, u3} = User.follow(u3, u1)
1864 {:ok, u1} = User.follow(u1, u4)
1865 {:ok, u4} = User.follow(u4, u1)
1866
1867 {:ok, u2} = User.follow(u2, u3)
1868 {:ok, u3} = User.follow(u3, u2)
1869
1870 {:ok, a1} = CommonAPI.post(u1, %{status: "Status", visibility: "private"})
1871
1872 {:ok, r1_1} =
1873 CommonAPI.post(u2, %{
1874 status: "@#{u1.nickname} reply from u2 to u1",
1875 in_reply_to_status_id: a1.id,
1876 visibility: "private"
1877 })
1878
1879 {:ok, r1_2} =
1880 CommonAPI.post(u3, %{
1881 status: "@#{u1.nickname} reply from u3 to u1",
1882 in_reply_to_status_id: a1.id,
1883 visibility: "private"
1884 })
1885
1886 {:ok, r1_3} =
1887 CommonAPI.post(u4, %{
1888 status: "@#{u1.nickname} reply from u4 to u1",
1889 in_reply_to_status_id: a1.id,
1890 visibility: "private"
1891 })
1892
1893 {:ok, a2} = CommonAPI.post(u2, %{status: "Status", visibility: "private"})
1894
1895 {:ok, r2_1} =
1896 CommonAPI.post(u1, %{
1897 status: "@#{u2.nickname} reply from u1 to u2",
1898 in_reply_to_status_id: a2.id,
1899 visibility: "private"
1900 })
1901
1902 {:ok, r2_2} =
1903 CommonAPI.post(u3, %{
1904 status: "@#{u2.nickname} reply from u3 to u2",
1905 in_reply_to_status_id: a2.id,
1906 visibility: "private"
1907 })
1908
1909 {:ok, a3} = CommonAPI.post(u3, %{status: "Status", visibility: "private"})
1910
1911 {:ok, r3_1} =
1912 CommonAPI.post(u1, %{
1913 status: "@#{u3.nickname} reply from u1 to u3",
1914 in_reply_to_status_id: a3.id,
1915 visibility: "private"
1916 })
1917
1918 {:ok, r3_2} =
1919 CommonAPI.post(u2, %{
1920 status: "@#{u3.nickname} reply from u2 to u3",
1921 in_reply_to_status_id: a3.id,
1922 visibility: "private"
1923 })
1924
1925 {:ok, a4} = CommonAPI.post(u4, %{status: "Status", visibility: "private"})
1926
1927 {:ok, r4_1} =
1928 CommonAPI.post(u1, %{
1929 status: "@#{u4.nickname} reply from u1 to u4",
1930 in_reply_to_status_id: a4.id,
1931 visibility: "private"
1932 })
1933
1934 {:ok,
1935 users: %{u1: u1, u2: u2, u3: u3, u4: u4},
1936 activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id},
1937 u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id},
1938 u2: %{r1: r2_1.id, r2: r2_2.id},
1939 u3: %{r1: r3_1.id, r2: r3_2.id},
1940 u4: %{r1: r4_1.id}}
1941 end
1942
1943 describe "maybe_update_follow_information/1" do
1944 setup do
1945 clear_config([:instance, :external_user_synchronization], true)
1946
1947 user = %{
1948 local: false,
1949 ap_id: "https://gensokyo.2hu/users/raymoo",
1950 following_address: "https://gensokyo.2hu/users/following",
1951 follower_address: "https://gensokyo.2hu/users/followers",
1952 type: "Person"
1953 }
1954
1955 %{user: user}
1956 end
1957
1958 test "logs an error when it can't fetch the info", %{user: user} do
1959 assert capture_log(fn ->
1960 ActivityPub.maybe_update_follow_information(user)
1961 end) =~ "Follower/Following counter update for #{user.ap_id} failed"
1962 end
1963
1964 test "just returns the input if the user type is Application", %{
1965 user: user
1966 } do
1967 user =
1968 user
1969 |> Map.put(:type, "Application")
1970
1971 refute capture_log(fn ->
1972 assert ^user = ActivityPub.maybe_update_follow_information(user)
1973 end) =~ "Follower/Following counter update for #{user.ap_id} failed"
1974 end
1975
1976 test "it just returns the input if the user has no following/follower addresses", %{
1977 user: user
1978 } do
1979 user =
1980 user
1981 |> Map.put(:following_address, nil)
1982 |> Map.put(:follower_address, nil)
1983
1984 refute capture_log(fn ->
1985 assert ^user = ActivityPub.maybe_update_follow_information(user)
1986 end) =~ "Follower/Following counter update for #{user.ap_id} failed"
1987 end
1988 end
1989 end