Merge branch 'require-signature' into 'develop'
[akkoma] / test / web / activity_pub / activity_pub_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 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.Notification
12 alias Pleroma.Object
13 alias Pleroma.User
14 alias Pleroma.Web.ActivityPub.ActivityPub
15 alias Pleroma.Web.ActivityPub.Utils
16 alias Pleroma.Web.AdminAPI.AccountView
17 alias Pleroma.Web.CommonAPI
18
19 import Pleroma.Factory
20 import Tesla.Mock
21 import Mock
22
23 setup do
24 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
25 :ok
26 end
27
28 clear_config([:instance, :federating])
29
30 describe "streaming out participations" do
31 test "it streams them out" do
32 user = insert(:user)
33 {:ok, activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
34
35 {:ok, conversation} = Pleroma.Conversation.create_or_bump_for(activity)
36
37 participations =
38 conversation.participations
39 |> Repo.preload(:user)
40
41 with_mock Pleroma.Web.Streamer,
42 stream: fn _, _ -> nil end do
43 ActivityPub.stream_out_participations(conversation.participations)
44
45 assert called(Pleroma.Web.Streamer.stream("participation", participations))
46 end
47 end
48
49 test "streams them out on activity creation" do
50 user_one = insert(:user)
51 user_two = insert(:user)
52
53 with_mock Pleroma.Web.Streamer,
54 stream: fn _, _ -> nil end do
55 {:ok, activity} =
56 CommonAPI.post(user_one, %{
57 "status" => "@#{user_two.nickname}",
58 "visibility" => "direct"
59 })
60
61 conversation =
62 activity.data["context"]
63 |> Pleroma.Conversation.get_for_ap_id()
64 |> Repo.preload(participations: :user)
65
66 assert called(Pleroma.Web.Streamer.stream("participation", conversation.participations))
67 end
68 end
69 end
70
71 describe "fetching restricted by visibility" do
72 test "it restricts by the appropriate visibility" do
73 user = insert(:user)
74
75 {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
76
77 {:ok, direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
78
79 {:ok, unlisted_activity} =
80 CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"})
81
82 {:ok, private_activity} =
83 CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
84
85 activities =
86 ActivityPub.fetch_activities([], %{:visibility => "direct", "actor_id" => user.ap_id})
87
88 assert activities == [direct_activity]
89
90 activities =
91 ActivityPub.fetch_activities([], %{:visibility => "unlisted", "actor_id" => user.ap_id})
92
93 assert activities == [unlisted_activity]
94
95 activities =
96 ActivityPub.fetch_activities([], %{:visibility => "private", "actor_id" => user.ap_id})
97
98 assert activities == [private_activity]
99
100 activities =
101 ActivityPub.fetch_activities([], %{:visibility => "public", "actor_id" => user.ap_id})
102
103 assert activities == [public_activity]
104
105 activities =
106 ActivityPub.fetch_activities([], %{
107 :visibility => ~w[private public],
108 "actor_id" => user.ap_id
109 })
110
111 assert activities == [public_activity, private_activity]
112 end
113 end
114
115 describe "fetching excluded by visibility" do
116 test "it excludes by the appropriate visibility" do
117 user = insert(:user)
118
119 {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
120
121 {:ok, direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
122
123 {:ok, unlisted_activity} =
124 CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"})
125
126 {:ok, private_activity} =
127 CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
128
129 activities =
130 ActivityPub.fetch_activities([], %{
131 "exclude_visibilities" => "direct",
132 "actor_id" => user.ap_id
133 })
134
135 assert public_activity in activities
136 assert unlisted_activity in activities
137 assert private_activity in activities
138 refute direct_activity in activities
139
140 activities =
141 ActivityPub.fetch_activities([], %{
142 "exclude_visibilities" => "unlisted",
143 "actor_id" => user.ap_id
144 })
145
146 assert public_activity in activities
147 refute unlisted_activity in activities
148 assert private_activity in activities
149 assert direct_activity in activities
150
151 activities =
152 ActivityPub.fetch_activities([], %{
153 "exclude_visibilities" => "private",
154 "actor_id" => user.ap_id
155 })
156
157 assert public_activity in activities
158 assert unlisted_activity in activities
159 refute private_activity in activities
160 assert direct_activity in activities
161
162 activities =
163 ActivityPub.fetch_activities([], %{
164 "exclude_visibilities" => "public",
165 "actor_id" => user.ap_id
166 })
167
168 refute public_activity in activities
169 assert unlisted_activity in activities
170 assert private_activity in activities
171 assert direct_activity in activities
172 end
173 end
174
175 describe "building a user from his ap id" do
176 test "it returns a user" do
177 user_id = "http://mastodon.example.org/users/admin"
178 {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
179 assert user.ap_id == user_id
180 assert user.nickname == "admin@mastodon.example.org"
181 assert user.source_data
182 assert user.ap_enabled
183 assert user.follower_address == "http://mastodon.example.org/users/admin/followers"
184 end
185
186 test "it returns a user that is invisible" do
187 user_id = "http://mastodon.example.org/users/relay"
188 {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
189 assert User.invisible?(user)
190 end
191
192 test "it fetches the appropriate tag-restricted posts" do
193 user = insert(:user)
194
195 {:ok, status_one} = CommonAPI.post(user, %{"status" => ". #test"})
196 {:ok, status_two} = CommonAPI.post(user, %{"status" => ". #essais"})
197 {:ok, status_three} = CommonAPI.post(user, %{"status" => ". #test #reject"})
198
199 fetch_one = ActivityPub.fetch_activities([], %{"type" => "Create", "tag" => "test"})
200
201 fetch_two =
202 ActivityPub.fetch_activities([], %{"type" => "Create", "tag" => ["test", "essais"]})
203
204 fetch_three =
205 ActivityPub.fetch_activities([], %{
206 "type" => "Create",
207 "tag" => ["test", "essais"],
208 "tag_reject" => ["reject"]
209 })
210
211 fetch_four =
212 ActivityPub.fetch_activities([], %{
213 "type" => "Create",
214 "tag" => ["test"],
215 "tag_all" => ["test", "reject"]
216 })
217
218 assert fetch_one == [status_one, status_three]
219 assert fetch_two == [status_one, status_two, status_three]
220 assert fetch_three == [status_one, status_two]
221 assert fetch_four == [status_three]
222 end
223 end
224
225 describe "insertion" do
226 test "drops activities beyond a certain limit" do
227 limit = Pleroma.Config.get([:instance, :remote_limit])
228
229 random_text =
230 :crypto.strong_rand_bytes(limit + 1)
231 |> Base.encode64()
232 |> binary_part(0, limit + 1)
233
234 data = %{
235 "ok" => true,
236 "object" => %{
237 "content" => random_text
238 }
239 }
240
241 assert {:error, {:remote_limit_error, _}} = ActivityPub.insert(data)
242 end
243
244 test "doesn't drop activities with content being null" do
245 user = insert(:user)
246
247 data = %{
248 "actor" => user.ap_id,
249 "to" => [],
250 "object" => %{
251 "actor" => user.ap_id,
252 "to" => [],
253 "type" => "Note",
254 "content" => nil
255 }
256 }
257
258 assert {:ok, _} = ActivityPub.insert(data)
259 end
260
261 test "returns the activity if one with the same id is already in" do
262 activity = insert(:note_activity)
263 {:ok, new_activity} = ActivityPub.insert(activity.data)
264
265 assert activity.id == new_activity.id
266 end
267
268 test "inserts a given map into the activity database, giving it an id if it has none." do
269 user = insert(:user)
270
271 data = %{
272 "actor" => user.ap_id,
273 "to" => [],
274 "object" => %{
275 "actor" => user.ap_id,
276 "to" => [],
277 "type" => "Note",
278 "content" => "hey"
279 }
280 }
281
282 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
283 assert activity.data["ok"] == data["ok"]
284 assert is_binary(activity.data["id"])
285
286 given_id = "bla"
287
288 data = %{
289 "id" => given_id,
290 "actor" => user.ap_id,
291 "to" => [],
292 "context" => "blabla",
293 "object" => %{
294 "actor" => user.ap_id,
295 "to" => [],
296 "type" => "Note",
297 "content" => "hey"
298 }
299 }
300
301 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
302 assert activity.data["ok"] == data["ok"]
303 assert activity.data["id"] == given_id
304 assert activity.data["context"] == "blabla"
305 assert activity.data["context_id"]
306 end
307
308 test "adds a context when none is there" do
309 user = insert(:user)
310
311 data = %{
312 "actor" => user.ap_id,
313 "to" => [],
314 "object" => %{
315 "actor" => user.ap_id,
316 "to" => [],
317 "type" => "Note",
318 "content" => "hey"
319 }
320 }
321
322 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
323 object = Pleroma.Object.normalize(activity)
324
325 assert is_binary(activity.data["context"])
326 assert is_binary(object.data["context"])
327 assert activity.data["context_id"]
328 assert object.data["context_id"]
329 end
330
331 test "adds an id to a given object if it lacks one and is a note and inserts it to the object database" do
332 user = insert(:user)
333
334 data = %{
335 "actor" => user.ap_id,
336 "to" => [],
337 "object" => %{
338 "actor" => user.ap_id,
339 "to" => [],
340 "type" => "Note",
341 "content" => "hey"
342 }
343 }
344
345 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
346 assert object = Object.normalize(activity)
347 assert is_binary(object.data["id"])
348 end
349 end
350
351 describe "listen activities" do
352 test "does not increase user note count" do
353 user = insert(:user)
354
355 {:ok, activity} =
356 ActivityPub.listen(%{
357 to: ["https://www.w3.org/ns/activitystreams#Public"],
358 actor: user,
359 context: "",
360 object: %{
361 "actor" => user.ap_id,
362 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
363 "artist" => "lain",
364 "title" => "lain radio episode 1",
365 "length" => 180_000,
366 "type" => "Audio"
367 }
368 })
369
370 assert activity.actor == user.ap_id
371
372 user = User.get_cached_by_id(user.id)
373 assert user.note_count == 0
374 end
375
376 test "can be fetched into a timeline" do
377 _listen_activity_1 = insert(:listen)
378 _listen_activity_2 = insert(:listen)
379 _listen_activity_3 = insert(:listen)
380
381 timeline = ActivityPub.fetch_activities([], %{"type" => ["Listen"]})
382
383 assert length(timeline) == 3
384 end
385 end
386
387 describe "create activities" do
388 test "removes doubled 'to' recipients" do
389 user = insert(:user)
390
391 {:ok, activity} =
392 ActivityPub.create(%{
393 to: ["user1", "user1", "user2"],
394 actor: user,
395 context: "",
396 object: %{
397 "to" => ["user1", "user1", "user2"],
398 "type" => "Note",
399 "content" => "testing"
400 }
401 })
402
403 assert activity.data["to"] == ["user1", "user2"]
404 assert activity.actor == user.ap_id
405 assert activity.recipients == ["user1", "user2", user.ap_id]
406 end
407
408 test "increases user note count only for public activities" do
409 user = insert(:user)
410
411 {:ok, _} =
412 CommonAPI.post(User.get_cached_by_id(user.id), %{
413 "status" => "1",
414 "visibility" => "public"
415 })
416
417 {:ok, _} =
418 CommonAPI.post(User.get_cached_by_id(user.id), %{
419 "status" => "2",
420 "visibility" => "unlisted"
421 })
422
423 {:ok, _} =
424 CommonAPI.post(User.get_cached_by_id(user.id), %{
425 "status" => "2",
426 "visibility" => "private"
427 })
428
429 {:ok, _} =
430 CommonAPI.post(User.get_cached_by_id(user.id), %{
431 "status" => "3",
432 "visibility" => "direct"
433 })
434
435 user = User.get_cached_by_id(user.id)
436 assert user.note_count == 2
437 end
438
439 test "increases replies count" do
440 user = insert(:user)
441 user2 = insert(:user)
442
443 {:ok, activity} = CommonAPI.post(user, %{"status" => "1", "visibility" => "public"})
444 ap_id = activity.data["id"]
445 reply_data = %{"status" => "1", "in_reply_to_status_id" => activity.id}
446
447 # public
448 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "public"))
449 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
450 assert object.data["repliesCount"] == 1
451
452 # unlisted
453 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "unlisted"))
454 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
455 assert object.data["repliesCount"] == 2
456
457 # private
458 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "private"))
459 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
460 assert object.data["repliesCount"] == 2
461
462 # direct
463 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct"))
464 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
465 assert object.data["repliesCount"] == 2
466 end
467 end
468
469 describe "fetch activities for recipients" do
470 test "retrieve the activities for certain recipients" do
471 {:ok, activity_one} = ActivityBuilder.insert(%{"to" => ["someone"]})
472 {:ok, activity_two} = ActivityBuilder.insert(%{"to" => ["someone_else"]})
473 {:ok, _activity_three} = ActivityBuilder.insert(%{"to" => ["noone"]})
474
475 activities = ActivityPub.fetch_activities(["someone", "someone_else"])
476 assert length(activities) == 2
477 assert activities == [activity_one, activity_two]
478 end
479 end
480
481 describe "fetch activities in context" do
482 test "retrieves activities that have a given context" do
483 {:ok, activity} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
484 {:ok, activity_two} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
485 {:ok, _activity_three} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"})
486 {:ok, _activity_four} = ActivityBuilder.insert(%{"type" => "Announce", "context" => "2hu"})
487 activity_five = insert(:note_activity)
488 user = insert(:user)
489
490 {:ok, _user_relationship} = User.block(user, %{ap_id: activity_five.data["actor"]})
491
492 activities = ActivityPub.fetch_activities_for_context("2hu", %{"blocking_user" => user})
493 assert activities == [activity_two, activity]
494 end
495 end
496
497 test "doesn't return blocked activities" do
498 activity_one = insert(:note_activity)
499 activity_two = insert(:note_activity)
500 activity_three = insert(:note_activity)
501 user = insert(:user)
502 booster = insert(:user)
503 {:ok, _user_relationship} = User.block(user, %{ap_id: activity_one.data["actor"]})
504
505 activities =
506 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
507
508 assert Enum.member?(activities, activity_two)
509 assert Enum.member?(activities, activity_three)
510 refute Enum.member?(activities, activity_one)
511
512 {:ok, _user_block} = User.unblock(user, %{ap_id: activity_one.data["actor"]})
513
514 activities =
515 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
516
517 assert Enum.member?(activities, activity_two)
518 assert Enum.member?(activities, activity_three)
519 assert Enum.member?(activities, activity_one)
520
521 {:ok, _user_relationship} = User.block(user, %{ap_id: activity_three.data["actor"]})
522 {:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
523 %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
524 activity_three = Activity.get_by_id(activity_three.id)
525
526 activities =
527 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
528
529 assert Enum.member?(activities, activity_two)
530 refute Enum.member?(activities, activity_three)
531 refute Enum.member?(activities, boost_activity)
532 assert Enum.member?(activities, activity_one)
533
534 activities =
535 ActivityPub.fetch_activities([], %{"blocking_user" => nil, "skip_preload" => true})
536
537 assert Enum.member?(activities, activity_two)
538 assert Enum.member?(activities, activity_three)
539 assert Enum.member?(activities, boost_activity)
540 assert Enum.member?(activities, activity_one)
541 end
542
543 test "doesn't return transitive interactions concerning blocked users" do
544 blocker = insert(:user)
545 blockee = insert(:user)
546 friend = insert(:user)
547
548 {:ok, _user_relationship} = User.block(blocker, blockee)
549
550 {:ok, activity_one} = CommonAPI.post(friend, %{"status" => "hey!"})
551
552 {:ok, activity_two} = CommonAPI.post(friend, %{"status" => "hey! @#{blockee.nickname}"})
553
554 {:ok, activity_three} = CommonAPI.post(blockee, %{"status" => "hey! @#{friend.nickname}"})
555
556 {:ok, activity_four} = CommonAPI.post(blockee, %{"status" => "hey! @#{blocker.nickname}"})
557
558 activities = ActivityPub.fetch_activities([], %{"blocking_user" => blocker})
559
560 assert Enum.member?(activities, activity_one)
561 refute Enum.member?(activities, activity_two)
562 refute Enum.member?(activities, activity_three)
563 refute Enum.member?(activities, activity_four)
564 end
565
566 test "doesn't return announce activities concerning blocked users" do
567 blocker = insert(:user)
568 blockee = insert(:user)
569 friend = insert(:user)
570
571 {:ok, _user_relationship} = User.block(blocker, blockee)
572
573 {:ok, activity_one} = CommonAPI.post(friend, %{"status" => "hey!"})
574
575 {:ok, activity_two} = CommonAPI.post(blockee, %{"status" => "hey! @#{friend.nickname}"})
576
577 {:ok, activity_three, _} = CommonAPI.repeat(activity_two.id, friend)
578
579 activities =
580 ActivityPub.fetch_activities([], %{"blocking_user" => blocker})
581 |> Enum.map(fn act -> act.id end)
582
583 assert Enum.member?(activities, activity_one.id)
584 refute Enum.member?(activities, activity_two.id)
585 refute Enum.member?(activities, activity_three.id)
586 end
587
588 test "doesn't return activities from blocked domains" do
589 domain = "dogwhistle.zone"
590 domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"})
591 note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})
592 activity = insert(:note_activity, %{note: note})
593 user = insert(:user)
594 {:ok, user} = User.block_domain(user, domain)
595
596 activities =
597 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
598
599 refute activity in activities
600
601 followed_user = insert(:user)
602 ActivityPub.follow(user, followed_user)
603 {:ok, repeat_activity, _} = CommonAPI.repeat(activity.id, followed_user)
604
605 activities =
606 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
607
608 refute repeat_activity in activities
609 end
610
611 test "does return activities from followed users on blocked domains" do
612 domain = "meanies.social"
613 domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"})
614 blocker = insert(:user)
615
616 {:ok, blocker} = User.follow(blocker, domain_user)
617 {:ok, blocker} = User.block_domain(blocker, domain)
618
619 assert User.following?(blocker, domain_user)
620 assert User.blocks_domain?(blocker, domain_user)
621 refute User.blocks?(blocker, domain_user)
622
623 note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})
624 activity = insert(:note_activity, %{note: note})
625
626 activities =
627 ActivityPub.fetch_activities([], %{"blocking_user" => blocker, "skip_preload" => true})
628
629 assert activity in activities
630
631 # And check that if the guy we DO follow boosts someone else from their domain,
632 # that should be hidden
633 another_user = insert(:user, %{ap_id: "https://#{domain}/@meanie2"})
634 bad_note = insert(:note, %{data: %{"actor" => another_user.ap_id}})
635 bad_activity = insert(:note_activity, %{note: bad_note})
636 {:ok, repeat_activity, _} = CommonAPI.repeat(bad_activity.id, domain_user)
637
638 activities =
639 ActivityPub.fetch_activities([], %{"blocking_user" => blocker, "skip_preload" => true})
640
641 refute repeat_activity in activities
642 end
643
644 test "doesn't return muted activities" do
645 activity_one = insert(:note_activity)
646 activity_two = insert(:note_activity)
647 activity_three = insert(:note_activity)
648 user = insert(:user)
649 booster = insert(:user)
650
651 activity_one_actor = User.get_by_ap_id(activity_one.data["actor"])
652 {:ok, _user_relationships} = User.mute(user, activity_one_actor)
653
654 activities =
655 ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
656
657 assert Enum.member?(activities, activity_two)
658 assert Enum.member?(activities, activity_three)
659 refute Enum.member?(activities, activity_one)
660
661 # Calling with 'with_muted' will deliver muted activities, too.
662 activities =
663 ActivityPub.fetch_activities([], %{
664 "muting_user" => user,
665 "with_muted" => true,
666 "skip_preload" => true
667 })
668
669 assert Enum.member?(activities, activity_two)
670 assert Enum.member?(activities, activity_three)
671 assert Enum.member?(activities, activity_one)
672
673 {:ok, _user_mute} = User.unmute(user, activity_one_actor)
674
675 activities =
676 ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
677
678 assert Enum.member?(activities, activity_two)
679 assert Enum.member?(activities, activity_three)
680 assert Enum.member?(activities, activity_one)
681
682 activity_three_actor = User.get_by_ap_id(activity_three.data["actor"])
683 {:ok, _user_relationships} = User.mute(user, activity_three_actor)
684 {:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
685 %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
686 activity_three = Activity.get_by_id(activity_three.id)
687
688 activities =
689 ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
690
691 assert Enum.member?(activities, activity_two)
692 refute Enum.member?(activities, activity_three)
693 refute Enum.member?(activities, boost_activity)
694 assert Enum.member?(activities, activity_one)
695
696 activities = ActivityPub.fetch_activities([], %{"muting_user" => nil, "skip_preload" => true})
697
698 assert Enum.member?(activities, activity_two)
699 assert Enum.member?(activities, activity_three)
700 assert Enum.member?(activities, boost_activity)
701 assert Enum.member?(activities, activity_one)
702 end
703
704 test "doesn't return thread muted activities" do
705 user = insert(:user)
706 _activity_one = insert(:note_activity)
707 note_two = insert(:note, data: %{"context" => "suya.."})
708 activity_two = insert(:note_activity, note: note_two)
709
710 {:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
711
712 assert [_activity_one] = ActivityPub.fetch_activities([], %{"muting_user" => user})
713 end
714
715 test "returns thread muted activities when with_muted is set" do
716 user = insert(:user)
717 _activity_one = insert(:note_activity)
718 note_two = insert(:note, data: %{"context" => "suya.."})
719 activity_two = insert(:note_activity, note: note_two)
720
721 {:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
722
723 assert [_activity_two, _activity_one] =
724 ActivityPub.fetch_activities([], %{"muting_user" => user, "with_muted" => true})
725 end
726
727 test "does include announces on request" do
728 activity_three = insert(:note_activity)
729 user = insert(:user)
730 booster = insert(:user)
731
732 {:ok, user} = User.follow(user, booster)
733
734 {:ok, announce, _object} = CommonAPI.repeat(activity_three.id, booster)
735
736 [announce_activity] = ActivityPub.fetch_activities([user.ap_id | User.following(user)])
737
738 assert announce_activity.id == announce.id
739 end
740
741 test "excludes reblogs on request" do
742 user = insert(:user)
743 {:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Create"}, %{:user => user})
744 {:ok, _} = ActivityBuilder.insert(%{"type" => "Announce"}, %{:user => user})
745
746 [activity] = ActivityPub.fetch_user_activities(user, nil, %{"exclude_reblogs" => "true"})
747
748 assert activity == expected_activity
749 end
750
751 describe "public fetch activities" do
752 test "doesn't retrieve unlisted activities" do
753 user = insert(:user)
754
755 {:ok, _unlisted_activity} =
756 CommonAPI.post(user, %{"status" => "yeah", "visibility" => "unlisted"})
757
758 {:ok, listed_activity} = CommonAPI.post(user, %{"status" => "yeah"})
759
760 [activity] = ActivityPub.fetch_public_activities()
761
762 assert activity == listed_activity
763 end
764
765 test "retrieves public activities" do
766 _activities = ActivityPub.fetch_public_activities()
767
768 %{public: public} = ActivityBuilder.public_and_non_public()
769
770 activities = ActivityPub.fetch_public_activities()
771 assert length(activities) == 1
772 assert Enum.at(activities, 0) == public
773 end
774
775 test "retrieves a maximum of 20 activities" do
776 ActivityBuilder.insert_list(10)
777 expected_activities = ActivityBuilder.insert_list(20)
778
779 activities = ActivityPub.fetch_public_activities()
780
781 assert collect_ids(activities) == collect_ids(expected_activities)
782 assert length(activities) == 20
783 end
784
785 test "retrieves ids starting from a since_id" do
786 activities = ActivityBuilder.insert_list(30)
787 expected_activities = ActivityBuilder.insert_list(10)
788 since_id = List.last(activities).id
789
790 activities = ActivityPub.fetch_public_activities(%{"since_id" => since_id})
791
792 assert collect_ids(activities) == collect_ids(expected_activities)
793 assert length(activities) == 10
794 end
795
796 test "retrieves ids up to max_id" do
797 ActivityBuilder.insert_list(10)
798 expected_activities = ActivityBuilder.insert_list(20)
799
800 %{id: max_id} =
801 10
802 |> ActivityBuilder.insert_list()
803 |> List.first()
804
805 activities = ActivityPub.fetch_public_activities(%{"max_id" => max_id})
806
807 assert length(activities) == 20
808 assert collect_ids(activities) == collect_ids(expected_activities)
809 end
810
811 test "paginates via offset/limit" do
812 _first_part_activities = ActivityBuilder.insert_list(10)
813 second_part_activities = ActivityBuilder.insert_list(10)
814
815 later_activities = ActivityBuilder.insert_list(10)
816
817 activities =
818 ActivityPub.fetch_public_activities(%{"page" => "2", "page_size" => "20"}, :offset)
819
820 assert length(activities) == 20
821
822 assert collect_ids(activities) ==
823 collect_ids(second_part_activities) ++ collect_ids(later_activities)
824 end
825
826 test "doesn't return reblogs for users for whom reblogs have been muted" do
827 activity = insert(:note_activity)
828 user = insert(:user)
829 booster = insert(:user)
830 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)
831
832 {:ok, activity, _} = CommonAPI.repeat(activity.id, booster)
833
834 activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
835
836 refute Enum.any?(activities, fn %{id: id} -> id == activity.id end)
837 end
838
839 test "returns reblogs for users for whom reblogs have not been muted" do
840 activity = insert(:note_activity)
841 user = insert(:user)
842 booster = insert(:user)
843 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)
844 {:ok, _reblog_mute} = CommonAPI.show_reblogs(user, booster)
845
846 {:ok, activity, _} = CommonAPI.repeat(activity.id, booster)
847
848 activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
849
850 assert Enum.any?(activities, fn %{id: id} -> id == activity.id end)
851 end
852 end
853
854 describe "react to an object" do
855 test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
856 Pleroma.Config.put([:instance, :federating], true)
857 user = insert(:user)
858 reactor = insert(:user)
859 {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
860 assert object = Object.normalize(activity)
861
862 {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥")
863
864 assert called(Pleroma.Web.Federator.publish(reaction_activity))
865 end
866
867 test "adds an emoji reaction activity to the db" do
868 user = insert(:user)
869 reactor = insert(:user)
870 third_user = insert(:user)
871 fourth_user = insert(:user)
872 {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
873 assert object = Object.normalize(activity)
874
875 {:ok, reaction_activity, object} = ActivityPub.react_with_emoji(reactor, object, "🔥")
876
877 assert reaction_activity
878
879 assert reaction_activity.data["actor"] == reactor.ap_id
880 assert reaction_activity.data["type"] == "EmojiReact"
881 assert reaction_activity.data["content"] == "🔥"
882 assert reaction_activity.data["object"] == object.data["id"]
883 assert reaction_activity.data["to"] == [User.ap_followers(reactor), activity.data["actor"]]
884 assert reaction_activity.data["context"] == object.data["context"]
885 assert object.data["reaction_count"] == 1
886 assert object.data["reactions"] == [["🔥", [reactor.ap_id]]]
887
888 {:ok, _reaction_activity, object} = ActivityPub.react_with_emoji(third_user, object, "☕")
889
890 assert object.data["reaction_count"] == 2
891 assert object.data["reactions"] == [["🔥", [reactor.ap_id]], ["☕", [third_user.ap_id]]]
892
893 {:ok, _reaction_activity, object} = ActivityPub.react_with_emoji(fourth_user, object, "🔥")
894
895 assert object.data["reaction_count"] == 3
896
897 assert object.data["reactions"] == [
898 ["🔥", [fourth_user.ap_id, reactor.ap_id]],
899 ["☕", [third_user.ap_id]]
900 ]
901 end
902 end
903
904 describe "unreacting to an object" do
905 test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
906 Pleroma.Config.put([:instance, :federating], true)
907 user = insert(:user)
908 reactor = insert(:user)
909 {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
910 assert object = Object.normalize(activity)
911
912 {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥")
913
914 assert called(Pleroma.Web.Federator.publish(reaction_activity))
915
916 {:ok, unreaction_activity, _object} =
917 ActivityPub.unreact_with_emoji(reactor, reaction_activity.data["id"])
918
919 assert called(Pleroma.Web.Federator.publish(unreaction_activity))
920 end
921
922 test "adds an undo activity to the db" do
923 user = insert(:user)
924 reactor = insert(:user)
925 {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
926 assert object = Object.normalize(activity)
927
928 {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥")
929
930 {:ok, unreaction_activity, _object} =
931 ActivityPub.unreact_with_emoji(reactor, reaction_activity.data["id"])
932
933 assert unreaction_activity.actor == reactor.ap_id
934 assert unreaction_activity.data["object"] == reaction_activity.data["id"]
935
936 object = Object.get_by_ap_id(object.data["id"])
937 assert object.data["reaction_count"] == 0
938 assert object.data["reactions"] == []
939 end
940 end
941
942 describe "like an object" do
943 test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
944 Pleroma.Config.put([:instance, :federating], true)
945 note_activity = insert(:note_activity)
946 assert object_activity = Object.normalize(note_activity)
947
948 user = insert(:user)
949
950 {:ok, like_activity, _object} = ActivityPub.like(user, object_activity)
951 assert called(Pleroma.Web.Federator.publish(like_activity))
952 end
953
954 test "returns exist activity if object already liked" do
955 note_activity = insert(:note_activity)
956 assert object_activity = Object.normalize(note_activity)
957
958 user = insert(:user)
959
960 {:ok, like_activity, _object} = ActivityPub.like(user, object_activity)
961
962 {:ok, like_activity_exist, _object} = ActivityPub.like(user, object_activity)
963 assert like_activity == like_activity_exist
964 end
965
966 test "adds a like activity to the db" do
967 note_activity = insert(:note_activity)
968 assert object = Object.normalize(note_activity)
969
970 user = insert(:user)
971 user_two = insert(:user)
972
973 {:ok, like_activity, object} = ActivityPub.like(user, object)
974
975 assert like_activity.data["actor"] == user.ap_id
976 assert like_activity.data["type"] == "Like"
977 assert like_activity.data["object"] == object.data["id"]
978 assert like_activity.data["to"] == [User.ap_followers(user), note_activity.data["actor"]]
979 assert like_activity.data["context"] == object.data["context"]
980 assert object.data["like_count"] == 1
981 assert object.data["likes"] == [user.ap_id]
982
983 # Just return the original activity if the user already liked it.
984 {:ok, same_like_activity, object} = ActivityPub.like(user, object)
985
986 assert like_activity == same_like_activity
987 assert object.data["likes"] == [user.ap_id]
988 assert object.data["like_count"] == 1
989
990 {:ok, _like_activity, object} = ActivityPub.like(user_two, object)
991 assert object.data["like_count"] == 2
992 end
993 end
994
995 describe "unliking" do
996 test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
997 Pleroma.Config.put([:instance, :federating], true)
998
999 note_activity = insert(:note_activity)
1000 object = Object.normalize(note_activity)
1001 user = insert(:user)
1002
1003 {:ok, object} = ActivityPub.unlike(user, object)
1004 refute called(Pleroma.Web.Federator.publish())
1005
1006 {:ok, _like_activity, object} = ActivityPub.like(user, object)
1007 assert object.data["like_count"] == 1
1008
1009 {:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object)
1010 assert object.data["like_count"] == 0
1011
1012 assert called(Pleroma.Web.Federator.publish(unlike_activity))
1013 end
1014
1015 test "unliking a previously liked object" do
1016 note_activity = insert(:note_activity)
1017 object = Object.normalize(note_activity)
1018 user = insert(:user)
1019
1020 # Unliking something that hasn't been liked does nothing
1021 {:ok, object} = ActivityPub.unlike(user, object)
1022 assert object.data["like_count"] == 0
1023
1024 {:ok, like_activity, object} = ActivityPub.like(user, object)
1025 assert object.data["like_count"] == 1
1026
1027 {:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object)
1028 assert object.data["like_count"] == 0
1029
1030 assert Activity.get_by_id(like_activity.id) == nil
1031 assert note_activity.actor in unlike_activity.recipients
1032 end
1033 end
1034
1035 describe "announcing an object" do
1036 test "adds an announce activity to the db" do
1037 note_activity = insert(:note_activity)
1038 object = Object.normalize(note_activity)
1039 user = insert(:user)
1040
1041 {:ok, announce_activity, object} = ActivityPub.announce(user, object)
1042 assert object.data["announcement_count"] == 1
1043 assert object.data["announcements"] == [user.ap_id]
1044
1045 assert announce_activity.data["to"] == [
1046 User.ap_followers(user),
1047 note_activity.data["actor"]
1048 ]
1049
1050 assert announce_activity.data["object"] == object.data["id"]
1051 assert announce_activity.data["actor"] == user.ap_id
1052 assert announce_activity.data["context"] == object.data["context"]
1053 end
1054 end
1055
1056 describe "announcing a private object" do
1057 test "adds an announce activity to the db if the audience is not widened" do
1058 user = insert(:user)
1059 {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
1060 object = Object.normalize(note_activity)
1061
1062 {:ok, announce_activity, object} = ActivityPub.announce(user, object, nil, true, false)
1063
1064 assert announce_activity.data["to"] == [User.ap_followers(user)]
1065
1066 assert announce_activity.data["object"] == object.data["id"]
1067 assert announce_activity.data["actor"] == user.ap_id
1068 assert announce_activity.data["context"] == object.data["context"]
1069 end
1070
1071 test "does not add an announce activity to the db if the audience is widened" do
1072 user = insert(:user)
1073 {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
1074 object = Object.normalize(note_activity)
1075
1076 assert {:error, _} = ActivityPub.announce(user, object, nil, true, true)
1077 end
1078
1079 test "does not add an announce activity to the db if the announcer is not the author" do
1080 user = insert(:user)
1081 announcer = insert(:user)
1082 {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
1083 object = Object.normalize(note_activity)
1084
1085 assert {:error, _} = ActivityPub.announce(announcer, object, nil, true, false)
1086 end
1087 end
1088
1089 describe "unannouncing an object" do
1090 test "unannouncing a previously announced object" do
1091 note_activity = insert(:note_activity)
1092 object = Object.normalize(note_activity)
1093 user = insert(:user)
1094
1095 # Unannouncing an object that is not announced does nothing
1096 # {:ok, object} = ActivityPub.unannounce(user, object)
1097 # assert object.data["announcement_count"] == 0
1098
1099 {:ok, announce_activity, object} = ActivityPub.announce(user, object)
1100 assert object.data["announcement_count"] == 1
1101
1102 {:ok, unannounce_activity, object} = ActivityPub.unannounce(user, object)
1103 assert object.data["announcement_count"] == 0
1104
1105 assert unannounce_activity.data["to"] == [
1106 User.ap_followers(user),
1107 object.data["actor"]
1108 ]
1109
1110 assert unannounce_activity.data["type"] == "Undo"
1111 assert unannounce_activity.data["object"] == announce_activity.data
1112 assert unannounce_activity.data["actor"] == user.ap_id
1113 assert unannounce_activity.data["context"] == announce_activity.data["context"]
1114
1115 assert Activity.get_by_id(announce_activity.id) == nil
1116 end
1117 end
1118
1119 describe "uploading files" do
1120 test "copies the file to the configured folder" do
1121 file = %Plug.Upload{
1122 content_type: "image/jpg",
1123 path: Path.absname("test/fixtures/image.jpg"),
1124 filename: "an_image.jpg"
1125 }
1126
1127 {:ok, %Object{} = object} = ActivityPub.upload(file)
1128 assert object.data["name"] == "an_image.jpg"
1129 end
1130
1131 test "works with base64 encoded images" do
1132 file = %{
1133 "img" => data_uri()
1134 }
1135
1136 {:ok, %Object{}} = ActivityPub.upload(file)
1137 end
1138 end
1139
1140 describe "fetch the latest Follow" do
1141 test "fetches the latest Follow activity" do
1142 %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
1143 follower = Repo.get_by(User, ap_id: activity.data["actor"])
1144 followed = Repo.get_by(User, ap_id: activity.data["object"])
1145
1146 assert activity == Utils.fetch_latest_follow(follower, followed)
1147 end
1148 end
1149
1150 describe "following / unfollowing" do
1151 test "creates a follow activity" do
1152 follower = insert(:user)
1153 followed = insert(:user)
1154
1155 {:ok, activity} = ActivityPub.follow(follower, followed)
1156 assert activity.data["type"] == "Follow"
1157 assert activity.data["actor"] == follower.ap_id
1158 assert activity.data["object"] == followed.ap_id
1159 end
1160
1161 test "creates an undo activity for the last follow" do
1162 follower = insert(:user)
1163 followed = insert(:user)
1164
1165 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
1166 {:ok, activity} = ActivityPub.unfollow(follower, followed)
1167
1168 assert activity.data["type"] == "Undo"
1169 assert activity.data["actor"] == follower.ap_id
1170
1171 embedded_object = activity.data["object"]
1172 assert is_map(embedded_object)
1173 assert embedded_object["type"] == "Follow"
1174 assert embedded_object["object"] == followed.ap_id
1175 assert embedded_object["id"] == follow_activity.data["id"]
1176 end
1177
1178 test "creates an undo activity for a pending follow request" do
1179 follower = insert(:user)
1180 followed = insert(:user, %{locked: true})
1181
1182 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
1183 {:ok, activity} = ActivityPub.unfollow(follower, followed)
1184
1185 assert activity.data["type"] == "Undo"
1186 assert activity.data["actor"] == follower.ap_id
1187
1188 embedded_object = activity.data["object"]
1189 assert is_map(embedded_object)
1190 assert embedded_object["type"] == "Follow"
1191 assert embedded_object["object"] == followed.ap_id
1192 assert embedded_object["id"] == follow_activity.data["id"]
1193 end
1194 end
1195
1196 describe "blocking / unblocking" do
1197 test "creates a block activity" do
1198 blocker = insert(:user)
1199 blocked = insert(:user)
1200
1201 {:ok, activity} = ActivityPub.block(blocker, blocked)
1202
1203 assert activity.data["type"] == "Block"
1204 assert activity.data["actor"] == blocker.ap_id
1205 assert activity.data["object"] == blocked.ap_id
1206 end
1207
1208 test "creates an undo activity for the last block" do
1209 blocker = insert(:user)
1210 blocked = insert(:user)
1211
1212 {:ok, block_activity} = ActivityPub.block(blocker, blocked)
1213 {:ok, activity} = ActivityPub.unblock(blocker, blocked)
1214
1215 assert activity.data["type"] == "Undo"
1216 assert activity.data["actor"] == blocker.ap_id
1217
1218 embedded_object = activity.data["object"]
1219 assert is_map(embedded_object)
1220 assert embedded_object["type"] == "Block"
1221 assert embedded_object["object"] == blocked.ap_id
1222 assert embedded_object["id"] == block_activity.data["id"]
1223 end
1224 end
1225
1226 describe "deletion" do
1227 clear_config([:instance, :rewrite_policy])
1228
1229 test "it creates a delete activity and deletes the original object" do
1230 note = insert(:note_activity)
1231 object = Object.normalize(note)
1232 {:ok, delete} = ActivityPub.delete(object)
1233
1234 assert delete.data["type"] == "Delete"
1235 assert delete.data["actor"] == note.data["actor"]
1236 assert delete.data["object"] == object.data["id"]
1237
1238 assert Activity.get_by_id(delete.id) != nil
1239
1240 assert Repo.get(Object, object.id).data["type"] == "Tombstone"
1241 end
1242
1243 test "decrements user note count only for public activities" do
1244 user = insert(:user, note_count: 10)
1245
1246 {:ok, a1} =
1247 CommonAPI.post(User.get_cached_by_id(user.id), %{
1248 "status" => "yeah",
1249 "visibility" => "public"
1250 })
1251
1252 {:ok, a2} =
1253 CommonAPI.post(User.get_cached_by_id(user.id), %{
1254 "status" => "yeah",
1255 "visibility" => "unlisted"
1256 })
1257
1258 {:ok, a3} =
1259 CommonAPI.post(User.get_cached_by_id(user.id), %{
1260 "status" => "yeah",
1261 "visibility" => "private"
1262 })
1263
1264 {:ok, a4} =
1265 CommonAPI.post(User.get_cached_by_id(user.id), %{
1266 "status" => "yeah",
1267 "visibility" => "direct"
1268 })
1269
1270 {:ok, _} = Object.normalize(a1) |> ActivityPub.delete()
1271 {:ok, _} = Object.normalize(a2) |> ActivityPub.delete()
1272 {:ok, _} = Object.normalize(a3) |> ActivityPub.delete()
1273 {:ok, _} = Object.normalize(a4) |> ActivityPub.delete()
1274
1275 user = User.get_cached_by_id(user.id)
1276 assert user.note_count == 10
1277 end
1278
1279 test "it creates a delete activity and checks that it is also sent to users mentioned by the deleted object" do
1280 user = insert(:user)
1281 note = insert(:note_activity)
1282 object = Object.normalize(note)
1283
1284 {:ok, object} =
1285 object
1286 |> Object.change(%{
1287 data: %{
1288 "actor" => object.data["actor"],
1289 "id" => object.data["id"],
1290 "to" => [user.ap_id],
1291 "type" => "Note"
1292 }
1293 })
1294 |> Object.update_and_set_cache()
1295
1296 {:ok, delete} = ActivityPub.delete(object)
1297
1298 assert user.ap_id in delete.data["to"]
1299 end
1300
1301 test "decreases reply count" do
1302 user = insert(:user)
1303 user2 = insert(:user)
1304
1305 {:ok, activity} = CommonAPI.post(user, %{"status" => "1", "visibility" => "public"})
1306 reply_data = %{"status" => "1", "in_reply_to_status_id" => activity.id}
1307 ap_id = activity.data["id"]
1308
1309 {:ok, public_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "public"))
1310 {:ok, unlisted_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "unlisted"))
1311 {:ok, private_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "private"))
1312 {:ok, direct_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct"))
1313
1314 _ = CommonAPI.delete(direct_reply.id, user2)
1315 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1316 assert object.data["repliesCount"] == 2
1317
1318 _ = CommonAPI.delete(private_reply.id, user2)
1319 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1320 assert object.data["repliesCount"] == 2
1321
1322 _ = CommonAPI.delete(public_reply.id, user2)
1323 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1324 assert object.data["repliesCount"] == 1
1325
1326 _ = CommonAPI.delete(unlisted_reply.id, user2)
1327 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1328 assert object.data["repliesCount"] == 0
1329 end
1330
1331 test "it passes delete activity through MRF before deleting the object" do
1332 Pleroma.Config.put([:instance, :rewrite_policy], Pleroma.Web.ActivityPub.MRF.DropPolicy)
1333
1334 note = insert(:note_activity)
1335 object = Object.normalize(note)
1336
1337 {:error, {:reject, _}} = ActivityPub.delete(object)
1338
1339 assert Activity.get_by_id(note.id)
1340 assert Repo.get(Object, object.id).data["type"] == object.data["type"]
1341 end
1342 end
1343
1344 describe "timeline post-processing" do
1345 test "it filters broken threads" do
1346 user1 = insert(:user)
1347 user2 = insert(:user)
1348 user3 = insert(:user)
1349
1350 {:ok, user1} = User.follow(user1, user3)
1351 assert User.following?(user1, user3)
1352
1353 {:ok, user2} = User.follow(user2, user3)
1354 assert User.following?(user2, user3)
1355
1356 {:ok, user3} = User.follow(user3, user2)
1357 assert User.following?(user3, user2)
1358
1359 {:ok, public_activity} = CommonAPI.post(user3, %{"status" => "hi 1"})
1360
1361 {:ok, private_activity_1} =
1362 CommonAPI.post(user3, %{"status" => "hi 2", "visibility" => "private"})
1363
1364 {:ok, private_activity_2} =
1365 CommonAPI.post(user2, %{
1366 "status" => "hi 3",
1367 "visibility" => "private",
1368 "in_reply_to_status_id" => private_activity_1.id
1369 })
1370
1371 {:ok, private_activity_3} =
1372 CommonAPI.post(user3, %{
1373 "status" => "hi 4",
1374 "visibility" => "private",
1375 "in_reply_to_status_id" => private_activity_2.id
1376 })
1377
1378 activities =
1379 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)])
1380 |> Enum.map(fn a -> a.id end)
1381
1382 private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"])
1383
1384 assert [public_activity.id, private_activity_1.id, private_activity_3.id] == activities
1385
1386 assert length(activities) == 3
1387
1388 activities =
1389 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)], %{"user" => user1})
1390 |> Enum.map(fn a -> a.id end)
1391
1392 assert [public_activity.id, private_activity_1.id] == activities
1393 assert length(activities) == 2
1394 end
1395 end
1396
1397 describe "update" do
1398 clear_config([:instance, :max_pinned_statuses])
1399
1400 test "it creates an update activity with the new user data" do
1401 user = insert(:user)
1402 {:ok, user} = User.ensure_keys_present(user)
1403 user_data = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
1404
1405 {:ok, update} =
1406 ActivityPub.update(%{
1407 actor: user_data["id"],
1408 to: [user.follower_address],
1409 cc: [],
1410 object: user_data
1411 })
1412
1413 assert update.data["actor"] == user.ap_id
1414 assert update.data["to"] == [user.follower_address]
1415 assert embedded_object = update.data["object"]
1416 assert embedded_object["id"] == user_data["id"]
1417 assert embedded_object["type"] == user_data["type"]
1418 end
1419 end
1420
1421 test "returned pinned statuses" do
1422 Pleroma.Config.put([:instance, :max_pinned_statuses], 3)
1423 user = insert(:user)
1424
1425 {:ok, activity_one} = CommonAPI.post(user, %{"status" => "HI!!!"})
1426 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
1427 {:ok, activity_three} = CommonAPI.post(user, %{"status" => "HI!!!"})
1428
1429 CommonAPI.pin(activity_one.id, user)
1430 user = refresh_record(user)
1431
1432 CommonAPI.pin(activity_two.id, user)
1433 user = refresh_record(user)
1434
1435 CommonAPI.pin(activity_three.id, user)
1436 user = refresh_record(user)
1437
1438 activities = ActivityPub.fetch_user_activities(user, nil, %{"pinned" => "true"})
1439
1440 assert 3 = length(activities)
1441 end
1442
1443 describe "flag/1" do
1444 setup do
1445 reporter = insert(:user)
1446 target_account = insert(:user)
1447 content = "foobar"
1448 {:ok, activity} = CommonAPI.post(target_account, %{"status" => content})
1449 context = Utils.generate_context_id()
1450
1451 reporter_ap_id = reporter.ap_id
1452 target_ap_id = target_account.ap_id
1453 activity_ap_id = activity.data["id"]
1454
1455 activity_with_object = Activity.get_by_ap_id_with_object(activity_ap_id)
1456
1457 {:ok,
1458 %{
1459 reporter: reporter,
1460 context: context,
1461 target_account: target_account,
1462 reported_activity: activity,
1463 content: content,
1464 activity_ap_id: activity_ap_id,
1465 activity_with_object: activity_with_object,
1466 reporter_ap_id: reporter_ap_id,
1467 target_ap_id: target_ap_id
1468 }}
1469 end
1470
1471 test "it can create a Flag activity",
1472 %{
1473 reporter: reporter,
1474 context: context,
1475 target_account: target_account,
1476 reported_activity: reported_activity,
1477 content: content,
1478 activity_ap_id: activity_ap_id,
1479 activity_with_object: activity_with_object,
1480 reporter_ap_id: reporter_ap_id,
1481 target_ap_id: target_ap_id
1482 } do
1483 assert {:ok, activity} =
1484 ActivityPub.flag(%{
1485 actor: reporter,
1486 context: context,
1487 account: target_account,
1488 statuses: [reported_activity],
1489 content: content
1490 })
1491
1492 note_obj = %{
1493 "type" => "Note",
1494 "id" => activity_ap_id,
1495 "content" => content,
1496 "published" => activity_with_object.object.data["published"],
1497 "actor" => AccountView.render("show.json", %{user: target_account})
1498 }
1499
1500 assert %Activity{
1501 actor: ^reporter_ap_id,
1502 data: %{
1503 "type" => "Flag",
1504 "content" => ^content,
1505 "context" => ^context,
1506 "object" => [^target_ap_id, ^note_obj]
1507 }
1508 } = activity
1509 end
1510
1511 test_with_mock "strips status data from Flag, before federating it",
1512 %{
1513 reporter: reporter,
1514 context: context,
1515 target_account: target_account,
1516 reported_activity: reported_activity,
1517 content: content
1518 },
1519 Utils,
1520 [:passthrough],
1521 [] do
1522 {:ok, activity} =
1523 ActivityPub.flag(%{
1524 actor: reporter,
1525 context: context,
1526 account: target_account,
1527 statuses: [reported_activity],
1528 content: content
1529 })
1530
1531 new_data =
1532 put_in(activity.data, ["object"], [target_account.ap_id, reported_activity.data["id"]])
1533
1534 assert_called(Utils.maybe_federate(%{activity | data: new_data}))
1535 end
1536 end
1537
1538 test "fetch_activities/2 returns activities addressed to a list " do
1539 user = insert(:user)
1540 member = insert(:user)
1541 {:ok, list} = Pleroma.List.create("foo", user)
1542 {:ok, list} = Pleroma.List.follow(list, member)
1543
1544 {:ok, activity} =
1545 CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"})
1546
1547 activity = Repo.preload(activity, :bookmark)
1548 activity = %Activity{activity | thread_muted?: !!activity.thread_muted?}
1549
1550 assert ActivityPub.fetch_activities([], %{"user" => user}) == [activity]
1551 end
1552
1553 def data_uri do
1554 File.read!("test/fixtures/avatar_data_uri")
1555 end
1556
1557 describe "fetch_activities_bounded" do
1558 test "fetches private posts for followed users" do
1559 user = insert(:user)
1560
1561 {:ok, activity} =
1562 CommonAPI.post(user, %{
1563 "status" => "thought I looked cute might delete later :3",
1564 "visibility" => "private"
1565 })
1566
1567 [result] = ActivityPub.fetch_activities_bounded([user.follower_address], [])
1568 assert result.id == activity.id
1569 end
1570
1571 test "fetches only public posts for other users" do
1572 user = insert(:user)
1573 {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe", "visibility" => "public"})
1574
1575 {:ok, _private_activity} =
1576 CommonAPI.post(user, %{
1577 "status" => "why is tenshi eating a corndog so cute?",
1578 "visibility" => "private"
1579 })
1580
1581 [result] = ActivityPub.fetch_activities_bounded([], [user.follower_address])
1582 assert result.id == activity.id
1583 end
1584 end
1585
1586 describe "fetch_follow_information_for_user" do
1587 test "syncronizes following/followers counters" do
1588 user =
1589 insert(:user,
1590 local: false,
1591 follower_address: "http://localhost:4001/users/fuser2/followers",
1592 following_address: "http://localhost:4001/users/fuser2/following"
1593 )
1594
1595 {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
1596 assert info.follower_count == 527
1597 assert info.following_count == 267
1598 end
1599
1600 test "detects hidden followers" do
1601 mock(fn env ->
1602 case env.url do
1603 "http://localhost:4001/users/masto_closed/followers?page=1" ->
1604 %Tesla.Env{status: 403, body: ""}
1605
1606 _ ->
1607 apply(HttpRequestMock, :request, [env])
1608 end
1609 end)
1610
1611 user =
1612 insert(:user,
1613 local: false,
1614 follower_address: "http://localhost:4001/users/masto_closed/followers",
1615 following_address: "http://localhost:4001/users/masto_closed/following"
1616 )
1617
1618 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1619 assert follow_info.hide_followers == true
1620 assert follow_info.hide_follows == false
1621 end
1622
1623 test "detects hidden follows" do
1624 mock(fn env ->
1625 case env.url do
1626 "http://localhost:4001/users/masto_closed/following?page=1" ->
1627 %Tesla.Env{status: 403, body: ""}
1628
1629 _ ->
1630 apply(HttpRequestMock, :request, [env])
1631 end
1632 end)
1633
1634 user =
1635 insert(:user,
1636 local: false,
1637 follower_address: "http://localhost:4001/users/masto_closed/followers",
1638 following_address: "http://localhost:4001/users/masto_closed/following"
1639 )
1640
1641 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1642 assert follow_info.hide_followers == false
1643 assert follow_info.hide_follows == true
1644 end
1645
1646 test "detects hidden follows/followers for friendica" do
1647 user =
1648 insert(:user,
1649 local: false,
1650 follower_address: "http://localhost:8080/followers/fuser3",
1651 following_address: "http://localhost:8080/following/fuser3"
1652 )
1653
1654 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1655 assert follow_info.hide_followers == true
1656 assert follow_info.follower_count == 296
1657 assert follow_info.following_count == 32
1658 assert follow_info.hide_follows == true
1659 end
1660
1661 test "doesn't crash when follower and following counters are hidden" do
1662 mock(fn env ->
1663 case env.url do
1664 "http://localhost:4001/users/masto_hidden_counters/following" ->
1665 json(%{
1666 "@context" => "https://www.w3.org/ns/activitystreams",
1667 "id" => "http://localhost:4001/users/masto_hidden_counters/followers"
1668 })
1669
1670 "http://localhost:4001/users/masto_hidden_counters/following?page=1" ->
1671 %Tesla.Env{status: 403, body: ""}
1672
1673 "http://localhost:4001/users/masto_hidden_counters/followers" ->
1674 json(%{
1675 "@context" => "https://www.w3.org/ns/activitystreams",
1676 "id" => "http://localhost:4001/users/masto_hidden_counters/following"
1677 })
1678
1679 "http://localhost:4001/users/masto_hidden_counters/followers?page=1" ->
1680 %Tesla.Env{status: 403, body: ""}
1681 end
1682 end)
1683
1684 user =
1685 insert(:user,
1686 local: false,
1687 follower_address: "http://localhost:4001/users/masto_hidden_counters/followers",
1688 following_address: "http://localhost:4001/users/masto_hidden_counters/following"
1689 )
1690
1691 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1692
1693 assert follow_info.hide_followers == true
1694 assert follow_info.follower_count == 0
1695 assert follow_info.hide_follows == true
1696 assert follow_info.following_count == 0
1697 end
1698 end
1699
1700 describe "fetch_favourites/3" do
1701 test "returns a favourite activities sorted by adds to favorite" do
1702 user = insert(:user)
1703 other_user = insert(:user)
1704 user1 = insert(:user)
1705 user2 = insert(:user)
1706 {:ok, a1} = CommonAPI.post(user1, %{"status" => "bla"})
1707 {:ok, _a2} = CommonAPI.post(user2, %{"status" => "traps are happy"})
1708 {:ok, a3} = CommonAPI.post(user2, %{"status" => "Trees Are "})
1709 {:ok, a4} = CommonAPI.post(user2, %{"status" => "Agent Smith "})
1710 {:ok, a5} = CommonAPI.post(user1, %{"status" => "Red or Blue "})
1711
1712 {:ok, _, _} = CommonAPI.favorite(a4.id, user)
1713 {:ok, _, _} = CommonAPI.favorite(a3.id, other_user)
1714 {:ok, _, _} = CommonAPI.favorite(a3.id, user)
1715 {:ok, _, _} = CommonAPI.favorite(a5.id, other_user)
1716 {:ok, _, _} = CommonAPI.favorite(a5.id, user)
1717 {:ok, _, _} = CommonAPI.favorite(a4.id, other_user)
1718 {:ok, _, _} = CommonAPI.favorite(a1.id, user)
1719 {:ok, _, _} = CommonAPI.favorite(a1.id, other_user)
1720 result = ActivityPub.fetch_favourites(user)
1721
1722 assert Enum.map(result, & &1.id) == [a1.id, a5.id, a3.id, a4.id]
1723
1724 result = ActivityPub.fetch_favourites(user, %{"limit" => 2})
1725 assert Enum.map(result, & &1.id) == [a1.id, a5.id]
1726 end
1727 end
1728
1729 describe "Move activity" do
1730 test "create" do
1731 %{ap_id: old_ap_id} = old_user = insert(:user)
1732 %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
1733 follower = insert(:user)
1734 follower_move_opted_out = insert(:user, allow_following_move: false)
1735
1736 User.follow(follower, old_user)
1737 User.follow(follower_move_opted_out, old_user)
1738
1739 assert User.following?(follower, old_user)
1740 assert User.following?(follower_move_opted_out, old_user)
1741
1742 assert {:ok, activity} = ActivityPub.move(old_user, new_user)
1743
1744 assert %Activity{
1745 actor: ^old_ap_id,
1746 data: %{
1747 "actor" => ^old_ap_id,
1748 "object" => ^old_ap_id,
1749 "target" => ^new_ap_id,
1750 "type" => "Move"
1751 },
1752 local: true
1753 } = activity
1754
1755 params = %{
1756 "op" => "move_following",
1757 "origin_id" => old_user.id,
1758 "target_id" => new_user.id
1759 }
1760
1761 assert_enqueued(worker: Pleroma.Workers.BackgroundWorker, args: params)
1762
1763 Pleroma.Workers.BackgroundWorker.perform(params, nil)
1764
1765 refute User.following?(follower, old_user)
1766 assert User.following?(follower, new_user)
1767
1768 assert User.following?(follower_move_opted_out, old_user)
1769 refute User.following?(follower_move_opted_out, new_user)
1770
1771 activity = %Activity{activity | object: nil}
1772
1773 assert [%Notification{activity: ^activity}] =
1774 Notification.for_user(follower, %{with_move: true})
1775
1776 assert [%Notification{activity: ^activity}] =
1777 Notification.for_user(follower_move_opted_out, %{with_move: true})
1778 end
1779
1780 test "old user must be in the new user's `also_known_as` list" do
1781 old_user = insert(:user)
1782 new_user = insert(:user)
1783
1784 assert {:error, "Target account must have the origin in `alsoKnownAs`"} =
1785 ActivityPub.move(old_user, new_user)
1786 end
1787 end
1788 end