Merge branch 'develop' into issue/1383
[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"] == "EmojiReaction"
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 end
1178
1179 describe "blocking / unblocking" do
1180 test "creates a block activity" do
1181 blocker = insert(:user)
1182 blocked = insert(:user)
1183
1184 {:ok, activity} = ActivityPub.block(blocker, blocked)
1185
1186 assert activity.data["type"] == "Block"
1187 assert activity.data["actor"] == blocker.ap_id
1188 assert activity.data["object"] == blocked.ap_id
1189 end
1190
1191 test "creates an undo activity for the last block" do
1192 blocker = insert(:user)
1193 blocked = insert(:user)
1194
1195 {:ok, block_activity} = ActivityPub.block(blocker, blocked)
1196 {:ok, activity} = ActivityPub.unblock(blocker, blocked)
1197
1198 assert activity.data["type"] == "Undo"
1199 assert activity.data["actor"] == blocker.ap_id
1200
1201 embedded_object = activity.data["object"]
1202 assert is_map(embedded_object)
1203 assert embedded_object["type"] == "Block"
1204 assert embedded_object["object"] == blocked.ap_id
1205 assert embedded_object["id"] == block_activity.data["id"]
1206 end
1207 end
1208
1209 describe "deletion" do
1210 test "it creates a delete activity and deletes the original object" do
1211 note = insert(:note_activity)
1212 object = Object.normalize(note)
1213 {:ok, delete} = ActivityPub.delete(object)
1214
1215 assert delete.data["type"] == "Delete"
1216 assert delete.data["actor"] == note.data["actor"]
1217 assert delete.data["object"] == object.data["id"]
1218
1219 assert Activity.get_by_id(delete.id) != nil
1220
1221 assert Repo.get(Object, object.id).data["type"] == "Tombstone"
1222 end
1223
1224 test "decrements user note count only for public activities" do
1225 user = insert(:user, note_count: 10)
1226
1227 {:ok, a1} =
1228 CommonAPI.post(User.get_cached_by_id(user.id), %{
1229 "status" => "yeah",
1230 "visibility" => "public"
1231 })
1232
1233 {:ok, a2} =
1234 CommonAPI.post(User.get_cached_by_id(user.id), %{
1235 "status" => "yeah",
1236 "visibility" => "unlisted"
1237 })
1238
1239 {:ok, a3} =
1240 CommonAPI.post(User.get_cached_by_id(user.id), %{
1241 "status" => "yeah",
1242 "visibility" => "private"
1243 })
1244
1245 {:ok, a4} =
1246 CommonAPI.post(User.get_cached_by_id(user.id), %{
1247 "status" => "yeah",
1248 "visibility" => "direct"
1249 })
1250
1251 {:ok, _} = Object.normalize(a1) |> ActivityPub.delete()
1252 {:ok, _} = Object.normalize(a2) |> ActivityPub.delete()
1253 {:ok, _} = Object.normalize(a3) |> ActivityPub.delete()
1254 {:ok, _} = Object.normalize(a4) |> ActivityPub.delete()
1255
1256 user = User.get_cached_by_id(user.id)
1257 assert user.note_count == 10
1258 end
1259
1260 test "it creates a delete activity and checks that it is also sent to users mentioned by the deleted object" do
1261 user = insert(:user)
1262 note = insert(:note_activity)
1263 object = Object.normalize(note)
1264
1265 {:ok, object} =
1266 object
1267 |> Object.change(%{
1268 data: %{
1269 "actor" => object.data["actor"],
1270 "id" => object.data["id"],
1271 "to" => [user.ap_id],
1272 "type" => "Note"
1273 }
1274 })
1275 |> Object.update_and_set_cache()
1276
1277 {:ok, delete} = ActivityPub.delete(object)
1278
1279 assert user.ap_id in delete.data["to"]
1280 end
1281
1282 test "decreases reply count" do
1283 user = insert(:user)
1284 user2 = insert(:user)
1285
1286 {:ok, activity} = CommonAPI.post(user, %{"status" => "1", "visibility" => "public"})
1287 reply_data = %{"status" => "1", "in_reply_to_status_id" => activity.id}
1288 ap_id = activity.data["id"]
1289
1290 {:ok, public_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "public"))
1291 {:ok, unlisted_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "unlisted"))
1292 {:ok, private_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "private"))
1293 {:ok, direct_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct"))
1294
1295 _ = CommonAPI.delete(direct_reply.id, user2)
1296 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1297 assert object.data["repliesCount"] == 2
1298
1299 _ = CommonAPI.delete(private_reply.id, user2)
1300 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1301 assert object.data["repliesCount"] == 2
1302
1303 _ = CommonAPI.delete(public_reply.id, user2)
1304 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1305 assert object.data["repliesCount"] == 1
1306
1307 _ = CommonAPI.delete(unlisted_reply.id, user2)
1308 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1309 assert object.data["repliesCount"] == 0
1310 end
1311
1312 test "it passes delete activity through MRF before deleting the object" do
1313 rewrite_policy = Pleroma.Config.get([:instance, :rewrite_policy])
1314 Pleroma.Config.put([:instance, :rewrite_policy], Pleroma.Web.ActivityPub.MRF.DropPolicy)
1315
1316 on_exit(fn -> Pleroma.Config.put([:instance, :rewrite_policy], rewrite_policy) end)
1317
1318 note = insert(:note_activity)
1319 object = Object.normalize(note)
1320
1321 {:error, {:reject, _}} = ActivityPub.delete(object)
1322
1323 assert Activity.get_by_id(note.id)
1324 assert Repo.get(Object, object.id).data["type"] == object.data["type"]
1325 end
1326 end
1327
1328 describe "timeline post-processing" do
1329 test "it filters broken threads" do
1330 user1 = insert(:user)
1331 user2 = insert(:user)
1332 user3 = insert(:user)
1333
1334 {:ok, user1} = User.follow(user1, user3)
1335 assert User.following?(user1, user3)
1336
1337 {:ok, user2} = User.follow(user2, user3)
1338 assert User.following?(user2, user3)
1339
1340 {:ok, user3} = User.follow(user3, user2)
1341 assert User.following?(user3, user2)
1342
1343 {:ok, public_activity} = CommonAPI.post(user3, %{"status" => "hi 1"})
1344
1345 {:ok, private_activity_1} =
1346 CommonAPI.post(user3, %{"status" => "hi 2", "visibility" => "private"})
1347
1348 {:ok, private_activity_2} =
1349 CommonAPI.post(user2, %{
1350 "status" => "hi 3",
1351 "visibility" => "private",
1352 "in_reply_to_status_id" => private_activity_1.id
1353 })
1354
1355 {:ok, private_activity_3} =
1356 CommonAPI.post(user3, %{
1357 "status" => "hi 4",
1358 "visibility" => "private",
1359 "in_reply_to_status_id" => private_activity_2.id
1360 })
1361
1362 activities =
1363 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)])
1364 |> Enum.map(fn a -> a.id end)
1365
1366 private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"])
1367
1368 assert [public_activity.id, private_activity_1.id, private_activity_3.id] == activities
1369
1370 assert length(activities) == 3
1371
1372 activities =
1373 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)], %{"user" => user1})
1374 |> Enum.map(fn a -> a.id end)
1375
1376 assert [public_activity.id, private_activity_1.id] == activities
1377 assert length(activities) == 2
1378 end
1379 end
1380
1381 describe "update" do
1382 test "it creates an update activity with the new user data" do
1383 user = insert(:user)
1384 {:ok, user} = User.ensure_keys_present(user)
1385 user_data = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
1386
1387 {:ok, update} =
1388 ActivityPub.update(%{
1389 actor: user_data["id"],
1390 to: [user.follower_address],
1391 cc: [],
1392 object: user_data
1393 })
1394
1395 assert update.data["actor"] == user.ap_id
1396 assert update.data["to"] == [user.follower_address]
1397 assert embedded_object = update.data["object"]
1398 assert embedded_object["id"] == user_data["id"]
1399 assert embedded_object["type"] == user_data["type"]
1400 end
1401 end
1402
1403 test "returned pinned statuses" do
1404 Pleroma.Config.put([:instance, :max_pinned_statuses], 3)
1405 user = insert(:user)
1406
1407 {:ok, activity_one} = CommonAPI.post(user, %{"status" => "HI!!!"})
1408 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
1409 {:ok, activity_three} = CommonAPI.post(user, %{"status" => "HI!!!"})
1410
1411 CommonAPI.pin(activity_one.id, user)
1412 user = refresh_record(user)
1413
1414 CommonAPI.pin(activity_two.id, user)
1415 user = refresh_record(user)
1416
1417 CommonAPI.pin(activity_three.id, user)
1418 user = refresh_record(user)
1419
1420 activities = ActivityPub.fetch_user_activities(user, nil, %{"pinned" => "true"})
1421
1422 assert 3 = length(activities)
1423 end
1424
1425 describe "flag/1" do
1426 setup do
1427 reporter = insert(:user)
1428 target_account = insert(:user)
1429 content = "foobar"
1430 {:ok, activity} = CommonAPI.post(target_account, %{"status" => content})
1431 context = Utils.generate_context_id()
1432
1433 reporter_ap_id = reporter.ap_id
1434 target_ap_id = target_account.ap_id
1435 activity_ap_id = activity.data["id"]
1436
1437 activity_with_object = Activity.get_by_ap_id_with_object(activity_ap_id)
1438
1439 {:ok,
1440 %{
1441 reporter: reporter,
1442 context: context,
1443 target_account: target_account,
1444 reported_activity: activity,
1445 content: content,
1446 activity_ap_id: activity_ap_id,
1447 activity_with_object: activity_with_object,
1448 reporter_ap_id: reporter_ap_id,
1449 target_ap_id: target_ap_id
1450 }}
1451 end
1452
1453 test "it can create a Flag activity",
1454 %{
1455 reporter: reporter,
1456 context: context,
1457 target_account: target_account,
1458 reported_activity: reported_activity,
1459 content: content,
1460 activity_ap_id: activity_ap_id,
1461 activity_with_object: activity_with_object,
1462 reporter_ap_id: reporter_ap_id,
1463 target_ap_id: target_ap_id
1464 } do
1465 assert {:ok, activity} =
1466 ActivityPub.flag(%{
1467 actor: reporter,
1468 context: context,
1469 account: target_account,
1470 statuses: [reported_activity],
1471 content: content
1472 })
1473
1474 note_obj = %{
1475 "type" => "Note",
1476 "id" => activity_ap_id,
1477 "content" => content,
1478 "published" => activity_with_object.object.data["published"],
1479 "actor" => AccountView.render("show.json", %{user: target_account})
1480 }
1481
1482 assert %Activity{
1483 actor: ^reporter_ap_id,
1484 data: %{
1485 "type" => "Flag",
1486 "content" => ^content,
1487 "context" => ^context,
1488 "object" => [^target_ap_id, ^note_obj]
1489 }
1490 } = activity
1491 end
1492
1493 test_with_mock "strips status data from Flag, before federating it",
1494 %{
1495 reporter: reporter,
1496 context: context,
1497 target_account: target_account,
1498 reported_activity: reported_activity,
1499 content: content
1500 },
1501 Utils,
1502 [:passthrough],
1503 [] do
1504 {:ok, activity} =
1505 ActivityPub.flag(%{
1506 actor: reporter,
1507 context: context,
1508 account: target_account,
1509 statuses: [reported_activity],
1510 content: content
1511 })
1512
1513 new_data =
1514 put_in(activity.data, ["object"], [target_account.ap_id, reported_activity.data["id"]])
1515
1516 assert_called(Utils.maybe_federate(%{activity | data: new_data}))
1517 end
1518 end
1519
1520 test "fetch_activities/2 returns activities addressed to a list " do
1521 user = insert(:user)
1522 member = insert(:user)
1523 {:ok, list} = Pleroma.List.create("foo", user)
1524 {:ok, list} = Pleroma.List.follow(list, member)
1525
1526 {:ok, activity} =
1527 CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"})
1528
1529 activity = Repo.preload(activity, :bookmark)
1530 activity = %Activity{activity | thread_muted?: !!activity.thread_muted?}
1531
1532 assert ActivityPub.fetch_activities([], %{"user" => user}) == [activity]
1533 end
1534
1535 def data_uri do
1536 File.read!("test/fixtures/avatar_data_uri")
1537 end
1538
1539 describe "fetch_activities_bounded" do
1540 test "fetches private posts for followed users" do
1541 user = insert(:user)
1542
1543 {:ok, activity} =
1544 CommonAPI.post(user, %{
1545 "status" => "thought I looked cute might delete later :3",
1546 "visibility" => "private"
1547 })
1548
1549 [result] = ActivityPub.fetch_activities_bounded([user.follower_address], [])
1550 assert result.id == activity.id
1551 end
1552
1553 test "fetches only public posts for other users" do
1554 user = insert(:user)
1555 {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe", "visibility" => "public"})
1556
1557 {:ok, _private_activity} =
1558 CommonAPI.post(user, %{
1559 "status" => "why is tenshi eating a corndog so cute?",
1560 "visibility" => "private"
1561 })
1562
1563 [result] = ActivityPub.fetch_activities_bounded([], [user.follower_address])
1564 assert result.id == activity.id
1565 end
1566 end
1567
1568 describe "fetch_follow_information_for_user" do
1569 test "syncronizes following/followers counters" do
1570 user =
1571 insert(:user,
1572 local: false,
1573 follower_address: "http://localhost:4001/users/fuser2/followers",
1574 following_address: "http://localhost:4001/users/fuser2/following"
1575 )
1576
1577 {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
1578 assert info.follower_count == 527
1579 assert info.following_count == 267
1580 end
1581
1582 test "detects hidden followers" do
1583 mock(fn env ->
1584 case env.url do
1585 "http://localhost:4001/users/masto_closed/followers?page=1" ->
1586 %Tesla.Env{status: 403, body: ""}
1587
1588 _ ->
1589 apply(HttpRequestMock, :request, [env])
1590 end
1591 end)
1592
1593 user =
1594 insert(:user,
1595 local: false,
1596 follower_address: "http://localhost:4001/users/masto_closed/followers",
1597 following_address: "http://localhost:4001/users/masto_closed/following"
1598 )
1599
1600 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1601 assert follow_info.hide_followers == true
1602 assert follow_info.hide_follows == false
1603 end
1604
1605 test "detects hidden follows" do
1606 mock(fn env ->
1607 case env.url do
1608 "http://localhost:4001/users/masto_closed/following?page=1" ->
1609 %Tesla.Env{status: 403, body: ""}
1610
1611 _ ->
1612 apply(HttpRequestMock, :request, [env])
1613 end
1614 end)
1615
1616 user =
1617 insert(:user,
1618 local: false,
1619 follower_address: "http://localhost:4001/users/masto_closed/followers",
1620 following_address: "http://localhost:4001/users/masto_closed/following"
1621 )
1622
1623 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1624 assert follow_info.hide_followers == false
1625 assert follow_info.hide_follows == true
1626 end
1627
1628 test "detects hidden follows/followers for friendica" do
1629 user =
1630 insert(:user,
1631 local: false,
1632 follower_address: "http://localhost:8080/followers/fuser3",
1633 following_address: "http://localhost:8080/following/fuser3"
1634 )
1635
1636 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1637 assert follow_info.hide_followers == true
1638 assert follow_info.follower_count == 296
1639 assert follow_info.following_count == 32
1640 assert follow_info.hide_follows == true
1641 end
1642
1643 test "doesn't crash when follower and following counters are hidden" do
1644 mock(fn env ->
1645 case env.url do
1646 "http://localhost:4001/users/masto_hidden_counters/following" ->
1647 json(%{
1648 "@context" => "https://www.w3.org/ns/activitystreams",
1649 "id" => "http://localhost:4001/users/masto_hidden_counters/followers"
1650 })
1651
1652 "http://localhost:4001/users/masto_hidden_counters/following?page=1" ->
1653 %Tesla.Env{status: 403, body: ""}
1654
1655 "http://localhost:4001/users/masto_hidden_counters/followers" ->
1656 json(%{
1657 "@context" => "https://www.w3.org/ns/activitystreams",
1658 "id" => "http://localhost:4001/users/masto_hidden_counters/following"
1659 })
1660
1661 "http://localhost:4001/users/masto_hidden_counters/followers?page=1" ->
1662 %Tesla.Env{status: 403, body: ""}
1663 end
1664 end)
1665
1666 user =
1667 insert(:user,
1668 local: false,
1669 follower_address: "http://localhost:4001/users/masto_hidden_counters/followers",
1670 following_address: "http://localhost:4001/users/masto_hidden_counters/following"
1671 )
1672
1673 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1674
1675 assert follow_info.hide_followers == true
1676 assert follow_info.follower_count == 0
1677 assert follow_info.hide_follows == true
1678 assert follow_info.following_count == 0
1679 end
1680 end
1681
1682 describe "fetch_favourites/3" do
1683 test "returns a favourite activities sorted by adds to favorite" do
1684 user = insert(:user)
1685 other_user = insert(:user)
1686 user1 = insert(:user)
1687 user2 = insert(:user)
1688 {:ok, a1} = CommonAPI.post(user1, %{"status" => "bla"})
1689 {:ok, _a2} = CommonAPI.post(user2, %{"status" => "traps are happy"})
1690 {:ok, a3} = CommonAPI.post(user2, %{"status" => "Trees Are "})
1691 {:ok, a4} = CommonAPI.post(user2, %{"status" => "Agent Smith "})
1692 {:ok, a5} = CommonAPI.post(user1, %{"status" => "Red or Blue "})
1693
1694 {:ok, _, _} = CommonAPI.favorite(a4.id, user)
1695 {:ok, _, _} = CommonAPI.favorite(a3.id, other_user)
1696 {:ok, _, _} = CommonAPI.favorite(a3.id, user)
1697 {:ok, _, _} = CommonAPI.favorite(a5.id, other_user)
1698 {:ok, _, _} = CommonAPI.favorite(a5.id, user)
1699 {:ok, _, _} = CommonAPI.favorite(a4.id, other_user)
1700 {:ok, _, _} = CommonAPI.favorite(a1.id, user)
1701 {:ok, _, _} = CommonAPI.favorite(a1.id, other_user)
1702 result = ActivityPub.fetch_favourites(user)
1703
1704 assert Enum.map(result, & &1.id) == [a1.id, a5.id, a3.id, a4.id]
1705
1706 result = ActivityPub.fetch_favourites(user, %{"limit" => 2})
1707 assert Enum.map(result, & &1.id) == [a1.id, a5.id]
1708 end
1709 end
1710
1711 describe "Move activity" do
1712 test "create" do
1713 %{ap_id: old_ap_id} = old_user = insert(:user)
1714 %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
1715 follower = insert(:user)
1716 follower_move_opted_out = insert(:user, allow_following_move: false)
1717
1718 User.follow(follower, old_user)
1719 User.follow(follower_move_opted_out, old_user)
1720
1721 assert User.following?(follower, old_user)
1722 assert User.following?(follower_move_opted_out, old_user)
1723
1724 assert {:ok, activity} = ActivityPub.move(old_user, new_user)
1725
1726 assert %Activity{
1727 actor: ^old_ap_id,
1728 data: %{
1729 "actor" => ^old_ap_id,
1730 "object" => ^old_ap_id,
1731 "target" => ^new_ap_id,
1732 "type" => "Move"
1733 },
1734 local: true
1735 } = activity
1736
1737 params = %{
1738 "op" => "move_following",
1739 "origin_id" => old_user.id,
1740 "target_id" => new_user.id
1741 }
1742
1743 assert_enqueued(worker: Pleroma.Workers.BackgroundWorker, args: params)
1744
1745 Pleroma.Workers.BackgroundWorker.perform(params, nil)
1746
1747 refute User.following?(follower, old_user)
1748 assert User.following?(follower, new_user)
1749
1750 assert User.following?(follower_move_opted_out, old_user)
1751 refute User.following?(follower_move_opted_out, new_user)
1752
1753 activity = %Activity{activity | object: nil}
1754
1755 assert [%Notification{activity: ^activity}] =
1756 Notification.for_user(follower, %{with_move: true})
1757
1758 assert [%Notification{activity: ^activity}] =
1759 Notification.for_user(follower_move_opted_out, %{with_move: true})
1760 end
1761
1762 test "old user must be in the new user's `also_known_as` list" do
1763 old_user = insert(:user)
1764 new_user = insert(:user)
1765
1766 assert {:error, "Target account must have the origin in `alsoKnownAs`"} =
1767 ActivityPub.move(old_user, new_user)
1768 end
1769 end
1770 end