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