Merge branch 'develop' into issue/1411
[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 {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
871 assert object = Object.normalize(activity)
872
873 {:ok, reaction_activity, object} = ActivityPub.react_with_emoji(reactor, object, "🔥")
874
875 assert reaction_activity
876
877 assert reaction_activity.data["actor"] == reactor.ap_id
878 assert reaction_activity.data["type"] == "EmojiReaction"
879 assert reaction_activity.data["content"] == "🔥"
880 assert reaction_activity.data["object"] == object.data["id"]
881 assert reaction_activity.data["to"] == [User.ap_followers(reactor), activity.data["actor"]]
882 assert reaction_activity.data["context"] == object.data["context"]
883 assert object.data["reaction_count"] == 1
884 assert object.data["reactions"]["🔥"] == [reactor.ap_id]
885 end
886 end
887
888 describe "unreacting to an object" do
889 test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
890 Pleroma.Config.put([:instance, :federating], true)
891 user = insert(:user)
892 reactor = insert(:user)
893 {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
894 assert object = Object.normalize(activity)
895
896 {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥")
897
898 assert called(Pleroma.Web.Federator.publish(reaction_activity))
899
900 {:ok, unreaction_activity, _object} =
901 ActivityPub.unreact_with_emoji(reactor, reaction_activity.data["id"])
902
903 assert called(Pleroma.Web.Federator.publish(unreaction_activity))
904 end
905
906 test "adds an undo activity to the db" do
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 {:ok, unreaction_activity, _object} =
915 ActivityPub.unreact_with_emoji(reactor, reaction_activity.data["id"])
916
917 assert unreaction_activity.actor == reactor.ap_id
918 assert unreaction_activity.data["object"] == reaction_activity.data["id"]
919
920 object = Object.get_by_ap_id(object.data["id"])
921 assert object.data["reaction_count"] == 0
922 assert object.data["reactions"] == %{}
923 end
924 end
925
926 describe "like an object" do
927 test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
928 Pleroma.Config.put([:instance, :federating], true)
929 note_activity = insert(:note_activity)
930 assert object_activity = Object.normalize(note_activity)
931
932 user = insert(:user)
933
934 {:ok, like_activity, _object} = ActivityPub.like(user, object_activity)
935 assert called(Pleroma.Web.Federator.publish(like_activity))
936 end
937
938 test "returns exist activity if object already liked" do
939 note_activity = insert(:note_activity)
940 assert object_activity = Object.normalize(note_activity)
941
942 user = insert(:user)
943
944 {:ok, like_activity, _object} = ActivityPub.like(user, object_activity)
945
946 {:ok, like_activity_exist, _object} = ActivityPub.like(user, object_activity)
947 assert like_activity == like_activity_exist
948 end
949
950 test "adds a like activity to the db" do
951 note_activity = insert(:note_activity)
952 assert object = Object.normalize(note_activity)
953
954 user = insert(:user)
955 user_two = insert(:user)
956
957 {:ok, like_activity, object} = ActivityPub.like(user, object)
958
959 assert like_activity.data["actor"] == user.ap_id
960 assert like_activity.data["type"] == "Like"
961 assert like_activity.data["object"] == object.data["id"]
962 assert like_activity.data["to"] == [User.ap_followers(user), note_activity.data["actor"]]
963 assert like_activity.data["context"] == object.data["context"]
964 assert object.data["like_count"] == 1
965 assert object.data["likes"] == [user.ap_id]
966
967 # Just return the original activity if the user already liked it.
968 {:ok, same_like_activity, object} = ActivityPub.like(user, object)
969
970 assert like_activity == same_like_activity
971 assert object.data["likes"] == [user.ap_id]
972 assert object.data["like_count"] == 1
973
974 {:ok, _like_activity, object} = ActivityPub.like(user_two, object)
975 assert object.data["like_count"] == 2
976 end
977 end
978
979 describe "unliking" do
980 test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
981 Pleroma.Config.put([:instance, :federating], true)
982
983 note_activity = insert(:note_activity)
984 object = Object.normalize(note_activity)
985 user = insert(:user)
986
987 {:ok, object} = ActivityPub.unlike(user, object)
988 refute called(Pleroma.Web.Federator.publish())
989
990 {:ok, _like_activity, object} = ActivityPub.like(user, object)
991 assert object.data["like_count"] == 1
992
993 {:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object)
994 assert object.data["like_count"] == 0
995
996 assert called(Pleroma.Web.Federator.publish(unlike_activity))
997 end
998
999 test "unliking a previously liked object" do
1000 note_activity = insert(:note_activity)
1001 object = Object.normalize(note_activity)
1002 user = insert(:user)
1003
1004 # Unliking something that hasn't been liked does nothing
1005 {:ok, object} = ActivityPub.unlike(user, object)
1006 assert object.data["like_count"] == 0
1007
1008 {:ok, like_activity, object} = ActivityPub.like(user, object)
1009 assert object.data["like_count"] == 1
1010
1011 {:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object)
1012 assert object.data["like_count"] == 0
1013
1014 assert Activity.get_by_id(like_activity.id) == nil
1015 assert note_activity.actor in unlike_activity.recipients
1016 end
1017 end
1018
1019 describe "announcing an object" do
1020 test "adds an announce activity to the db" do
1021 note_activity = insert(:note_activity)
1022 object = Object.normalize(note_activity)
1023 user = insert(:user)
1024
1025 {:ok, announce_activity, object} = ActivityPub.announce(user, object)
1026 assert object.data["announcement_count"] == 1
1027 assert object.data["announcements"] == [user.ap_id]
1028
1029 assert announce_activity.data["to"] == [
1030 User.ap_followers(user),
1031 note_activity.data["actor"]
1032 ]
1033
1034 assert announce_activity.data["object"] == object.data["id"]
1035 assert announce_activity.data["actor"] == user.ap_id
1036 assert announce_activity.data["context"] == object.data["context"]
1037 end
1038 end
1039
1040 describe "announcing a private object" do
1041 test "adds an announce activity to the db if the audience is not widened" do
1042 user = insert(:user)
1043 {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
1044 object = Object.normalize(note_activity)
1045
1046 {:ok, announce_activity, object} = ActivityPub.announce(user, object, nil, true, false)
1047
1048 assert announce_activity.data["to"] == [User.ap_followers(user)]
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
1055 test "does not add an announce activity to the db if the audience is widened" do
1056 user = insert(:user)
1057 {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
1058 object = Object.normalize(note_activity)
1059
1060 assert {:error, _} = ActivityPub.announce(user, object, nil, true, true)
1061 end
1062
1063 test "does not add an announce activity to the db if the announcer is not the author" do
1064 user = insert(:user)
1065 announcer = insert(:user)
1066 {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
1067 object = Object.normalize(note_activity)
1068
1069 assert {:error, _} = ActivityPub.announce(announcer, object, nil, true, false)
1070 end
1071 end
1072
1073 describe "unannouncing an object" do
1074 test "unannouncing a previously announced object" do
1075 note_activity = insert(:note_activity)
1076 object = Object.normalize(note_activity)
1077 user = insert(:user)
1078
1079 # Unannouncing an object that is not announced does nothing
1080 # {:ok, object} = ActivityPub.unannounce(user, object)
1081 # assert object.data["announcement_count"] == 0
1082
1083 {:ok, announce_activity, object} = ActivityPub.announce(user, object)
1084 assert object.data["announcement_count"] == 1
1085
1086 {:ok, unannounce_activity, object} = ActivityPub.unannounce(user, object)
1087 assert object.data["announcement_count"] == 0
1088
1089 assert unannounce_activity.data["to"] == [
1090 User.ap_followers(user),
1091 object.data["actor"]
1092 ]
1093
1094 assert unannounce_activity.data["type"] == "Undo"
1095 assert unannounce_activity.data["object"] == announce_activity.data
1096 assert unannounce_activity.data["actor"] == user.ap_id
1097 assert unannounce_activity.data["context"] == announce_activity.data["context"]
1098
1099 assert Activity.get_by_id(announce_activity.id) == nil
1100 end
1101 end
1102
1103 describe "uploading files" do
1104 test "copies the file to the configured folder" do
1105 file = %Plug.Upload{
1106 content_type: "image/jpg",
1107 path: Path.absname("test/fixtures/image.jpg"),
1108 filename: "an_image.jpg"
1109 }
1110
1111 {:ok, %Object{} = object} = ActivityPub.upload(file)
1112 assert object.data["name"] == "an_image.jpg"
1113 end
1114
1115 test "works with base64 encoded images" do
1116 file = %{
1117 "img" => data_uri()
1118 }
1119
1120 {:ok, %Object{}} = ActivityPub.upload(file)
1121 end
1122 end
1123
1124 describe "fetch the latest Follow" do
1125 test "fetches the latest Follow activity" do
1126 %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
1127 follower = Repo.get_by(User, ap_id: activity.data["actor"])
1128 followed = Repo.get_by(User, ap_id: activity.data["object"])
1129
1130 assert activity == Utils.fetch_latest_follow(follower, followed)
1131 end
1132 end
1133
1134 describe "following / unfollowing" do
1135 test "creates a follow activity" do
1136 follower = insert(:user)
1137 followed = insert(:user)
1138
1139 {:ok, activity} = ActivityPub.follow(follower, followed)
1140 assert activity.data["type"] == "Follow"
1141 assert activity.data["actor"] == follower.ap_id
1142 assert activity.data["object"] == followed.ap_id
1143 end
1144
1145 test "creates an undo activity for the last follow" do
1146 follower = insert(:user)
1147 followed = insert(:user)
1148
1149 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
1150 {:ok, activity} = ActivityPub.unfollow(follower, followed)
1151
1152 assert activity.data["type"] == "Undo"
1153 assert activity.data["actor"] == follower.ap_id
1154
1155 embedded_object = activity.data["object"]
1156 assert is_map(embedded_object)
1157 assert embedded_object["type"] == "Follow"
1158 assert embedded_object["object"] == followed.ap_id
1159 assert embedded_object["id"] == follow_activity.data["id"]
1160 end
1161 end
1162
1163 describe "blocking / unblocking" do
1164 test "creates a block activity" do
1165 blocker = insert(:user)
1166 blocked = insert(:user)
1167
1168 {:ok, activity} = ActivityPub.block(blocker, blocked)
1169
1170 assert activity.data["type"] == "Block"
1171 assert activity.data["actor"] == blocker.ap_id
1172 assert activity.data["object"] == blocked.ap_id
1173 end
1174
1175 test "creates an undo activity for the last block" do
1176 blocker = insert(:user)
1177 blocked = insert(:user)
1178
1179 {:ok, block_activity} = ActivityPub.block(blocker, blocked)
1180 {:ok, activity} = ActivityPub.unblock(blocker, blocked)
1181
1182 assert activity.data["type"] == "Undo"
1183 assert activity.data["actor"] == blocker.ap_id
1184
1185 embedded_object = activity.data["object"]
1186 assert is_map(embedded_object)
1187 assert embedded_object["type"] == "Block"
1188 assert embedded_object["object"] == blocked.ap_id
1189 assert embedded_object["id"] == block_activity.data["id"]
1190 end
1191 end
1192
1193 describe "deletion" do
1194 test "it creates a delete activity and deletes the original object" do
1195 note = insert(:note_activity)
1196 object = Object.normalize(note)
1197 {:ok, delete} = ActivityPub.delete(object)
1198
1199 assert delete.data["type"] == "Delete"
1200 assert delete.data["actor"] == note.data["actor"]
1201 assert delete.data["object"] == object.data["id"]
1202
1203 assert Activity.get_by_id(delete.id) != nil
1204
1205 assert Repo.get(Object, object.id).data["type"] == "Tombstone"
1206 end
1207
1208 test "decrements user note count only for public activities" do
1209 user = insert(:user, note_count: 10)
1210
1211 {:ok, a1} =
1212 CommonAPI.post(User.get_cached_by_id(user.id), %{
1213 "status" => "yeah",
1214 "visibility" => "public"
1215 })
1216
1217 {:ok, a2} =
1218 CommonAPI.post(User.get_cached_by_id(user.id), %{
1219 "status" => "yeah",
1220 "visibility" => "unlisted"
1221 })
1222
1223 {:ok, a3} =
1224 CommonAPI.post(User.get_cached_by_id(user.id), %{
1225 "status" => "yeah",
1226 "visibility" => "private"
1227 })
1228
1229 {:ok, a4} =
1230 CommonAPI.post(User.get_cached_by_id(user.id), %{
1231 "status" => "yeah",
1232 "visibility" => "direct"
1233 })
1234
1235 {:ok, _} = Object.normalize(a1) |> ActivityPub.delete()
1236 {:ok, _} = Object.normalize(a2) |> ActivityPub.delete()
1237 {:ok, _} = Object.normalize(a3) |> ActivityPub.delete()
1238 {:ok, _} = Object.normalize(a4) |> ActivityPub.delete()
1239
1240 user = User.get_cached_by_id(user.id)
1241 assert user.note_count == 10
1242 end
1243
1244 test "it creates a delete activity and checks that it is also sent to users mentioned by the deleted object" do
1245 user = insert(:user)
1246 note = insert(:note_activity)
1247 object = Object.normalize(note)
1248
1249 {:ok, object} =
1250 object
1251 |> Object.change(%{
1252 data: %{
1253 "actor" => object.data["actor"],
1254 "id" => object.data["id"],
1255 "to" => [user.ap_id],
1256 "type" => "Note"
1257 }
1258 })
1259 |> Object.update_and_set_cache()
1260
1261 {:ok, delete} = ActivityPub.delete(object)
1262
1263 assert user.ap_id in delete.data["to"]
1264 end
1265
1266 test "decreases reply count" do
1267 user = insert(:user)
1268 user2 = insert(:user)
1269
1270 {:ok, activity} = CommonAPI.post(user, %{"status" => "1", "visibility" => "public"})
1271 reply_data = %{"status" => "1", "in_reply_to_status_id" => activity.id}
1272 ap_id = activity.data["id"]
1273
1274 {:ok, public_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "public"))
1275 {:ok, unlisted_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "unlisted"))
1276 {:ok, private_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "private"))
1277 {:ok, direct_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct"))
1278
1279 _ = CommonAPI.delete(direct_reply.id, user2)
1280 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1281 assert object.data["repliesCount"] == 2
1282
1283 _ = CommonAPI.delete(private_reply.id, user2)
1284 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1285 assert object.data["repliesCount"] == 2
1286
1287 _ = CommonAPI.delete(public_reply.id, user2)
1288 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1289 assert object.data["repliesCount"] == 1
1290
1291 _ = CommonAPI.delete(unlisted_reply.id, user2)
1292 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1293 assert object.data["repliesCount"] == 0
1294 end
1295
1296 test "it passes delete activity through MRF before deleting the object" do
1297 rewrite_policy = Pleroma.Config.get([:instance, :rewrite_policy])
1298 Pleroma.Config.put([:instance, :rewrite_policy], Pleroma.Web.ActivityPub.MRF.DropPolicy)
1299
1300 on_exit(fn -> Pleroma.Config.put([:instance, :rewrite_policy], rewrite_policy) end)
1301
1302 note = insert(:note_activity)
1303 object = Object.normalize(note)
1304
1305 {:error, {:reject, _}} = ActivityPub.delete(object)
1306
1307 assert Activity.get_by_id(note.id)
1308 assert Repo.get(Object, object.id).data["type"] == object.data["type"]
1309 end
1310 end
1311
1312 describe "timeline post-processing" do
1313 test "it filters broken threads" do
1314 user1 = insert(:user)
1315 user2 = insert(:user)
1316 user3 = insert(:user)
1317
1318 {:ok, user1} = User.follow(user1, user3)
1319 assert User.following?(user1, user3)
1320
1321 {:ok, user2} = User.follow(user2, user3)
1322 assert User.following?(user2, user3)
1323
1324 {:ok, user3} = User.follow(user3, user2)
1325 assert User.following?(user3, user2)
1326
1327 {:ok, public_activity} = CommonAPI.post(user3, %{"status" => "hi 1"})
1328
1329 {:ok, private_activity_1} =
1330 CommonAPI.post(user3, %{"status" => "hi 2", "visibility" => "private"})
1331
1332 {:ok, private_activity_2} =
1333 CommonAPI.post(user2, %{
1334 "status" => "hi 3",
1335 "visibility" => "private",
1336 "in_reply_to_status_id" => private_activity_1.id
1337 })
1338
1339 {:ok, private_activity_3} =
1340 CommonAPI.post(user3, %{
1341 "status" => "hi 4",
1342 "visibility" => "private",
1343 "in_reply_to_status_id" => private_activity_2.id
1344 })
1345
1346 activities =
1347 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)])
1348 |> Enum.map(fn a -> a.id end)
1349
1350 private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"])
1351
1352 assert [public_activity.id, private_activity_1.id, private_activity_3.id] == activities
1353
1354 assert length(activities) == 3
1355
1356 activities =
1357 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)], %{"user" => user1})
1358 |> Enum.map(fn a -> a.id end)
1359
1360 assert [public_activity.id, private_activity_1.id] == activities
1361 assert length(activities) == 2
1362 end
1363 end
1364
1365 describe "update" do
1366 test "it creates an update activity with the new user data" do
1367 user = insert(:user)
1368 {:ok, user} = User.ensure_keys_present(user)
1369 user_data = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
1370
1371 {:ok, update} =
1372 ActivityPub.update(%{
1373 actor: user_data["id"],
1374 to: [user.follower_address],
1375 cc: [],
1376 object: user_data
1377 })
1378
1379 assert update.data["actor"] == user.ap_id
1380 assert update.data["to"] == [user.follower_address]
1381 assert embedded_object = update.data["object"]
1382 assert embedded_object["id"] == user_data["id"]
1383 assert embedded_object["type"] == user_data["type"]
1384 end
1385 end
1386
1387 test "returned pinned statuses" do
1388 Pleroma.Config.put([:instance, :max_pinned_statuses], 3)
1389 user = insert(:user)
1390
1391 {:ok, activity_one} = CommonAPI.post(user, %{"status" => "HI!!!"})
1392 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
1393 {:ok, activity_three} = CommonAPI.post(user, %{"status" => "HI!!!"})
1394
1395 CommonAPI.pin(activity_one.id, user)
1396 user = refresh_record(user)
1397
1398 CommonAPI.pin(activity_two.id, user)
1399 user = refresh_record(user)
1400
1401 CommonAPI.pin(activity_three.id, user)
1402 user = refresh_record(user)
1403
1404 activities = ActivityPub.fetch_user_activities(user, nil, %{"pinned" => "true"})
1405
1406 assert 3 = length(activities)
1407 end
1408
1409 describe "flag/1" do
1410 setup do
1411 reporter = insert(:user)
1412 target_account = insert(:user)
1413 content = "foobar"
1414 {:ok, activity} = CommonAPI.post(target_account, %{"status" => content})
1415 context = Utils.generate_context_id()
1416
1417 reporter_ap_id = reporter.ap_id
1418 target_ap_id = target_account.ap_id
1419 activity_ap_id = activity.data["id"]
1420
1421 activity_with_object = Activity.get_by_ap_id_with_object(activity_ap_id)
1422
1423 {:ok,
1424 %{
1425 reporter: reporter,
1426 context: context,
1427 target_account: target_account,
1428 reported_activity: activity,
1429 content: content,
1430 activity_ap_id: activity_ap_id,
1431 activity_with_object: activity_with_object,
1432 reporter_ap_id: reporter_ap_id,
1433 target_ap_id: target_ap_id
1434 }}
1435 end
1436
1437 test "it can create a Flag activity",
1438 %{
1439 reporter: reporter,
1440 context: context,
1441 target_account: target_account,
1442 reported_activity: reported_activity,
1443 content: content,
1444 activity_ap_id: activity_ap_id,
1445 activity_with_object: activity_with_object,
1446 reporter_ap_id: reporter_ap_id,
1447 target_ap_id: target_ap_id
1448 } do
1449 assert {:ok, activity} =
1450 ActivityPub.flag(%{
1451 actor: reporter,
1452 context: context,
1453 account: target_account,
1454 statuses: [reported_activity],
1455 content: content
1456 })
1457
1458 note_obj = %{
1459 "type" => "Note",
1460 "id" => activity_ap_id,
1461 "content" => content,
1462 "published" => activity_with_object.object.data["published"],
1463 "actor" => AccountView.render("show.json", %{user: target_account})
1464 }
1465
1466 assert %Activity{
1467 actor: ^reporter_ap_id,
1468 data: %{
1469 "type" => "Flag",
1470 "content" => ^content,
1471 "context" => ^context,
1472 "object" => [^target_ap_id, ^note_obj]
1473 }
1474 } = activity
1475 end
1476
1477 test_with_mock "strips status data from Flag, before federating it",
1478 %{
1479 reporter: reporter,
1480 context: context,
1481 target_account: target_account,
1482 reported_activity: reported_activity,
1483 content: content
1484 },
1485 Utils,
1486 [:passthrough],
1487 [] do
1488 {:ok, activity} =
1489 ActivityPub.flag(%{
1490 actor: reporter,
1491 context: context,
1492 account: target_account,
1493 statuses: [reported_activity],
1494 content: content
1495 })
1496
1497 new_data =
1498 put_in(activity.data, ["object"], [target_account.ap_id, reported_activity.data["id"]])
1499
1500 assert_called(Utils.maybe_federate(%{activity | data: new_data}))
1501 end
1502 end
1503
1504 test "fetch_activities/2 returns activities addressed to a list " do
1505 user = insert(:user)
1506 member = insert(:user)
1507 {:ok, list} = Pleroma.List.create("foo", user)
1508 {:ok, list} = Pleroma.List.follow(list, member)
1509
1510 {:ok, activity} =
1511 CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"})
1512
1513 activity = Repo.preload(activity, :bookmark)
1514 activity = %Activity{activity | thread_muted?: !!activity.thread_muted?}
1515
1516 assert ActivityPub.fetch_activities([], %{"user" => user}) == [activity]
1517 end
1518
1519 def data_uri do
1520 File.read!("test/fixtures/avatar_data_uri")
1521 end
1522
1523 describe "fetch_activities_bounded" do
1524 test "fetches private posts for followed users" do
1525 user = insert(:user)
1526
1527 {:ok, activity} =
1528 CommonAPI.post(user, %{
1529 "status" => "thought I looked cute might delete later :3",
1530 "visibility" => "private"
1531 })
1532
1533 [result] = ActivityPub.fetch_activities_bounded([user.follower_address], [])
1534 assert result.id == activity.id
1535 end
1536
1537 test "fetches only public posts for other users" do
1538 user = insert(:user)
1539 {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe", "visibility" => "public"})
1540
1541 {:ok, _private_activity} =
1542 CommonAPI.post(user, %{
1543 "status" => "why is tenshi eating a corndog so cute?",
1544 "visibility" => "private"
1545 })
1546
1547 [result] = ActivityPub.fetch_activities_bounded([], [user.follower_address])
1548 assert result.id == activity.id
1549 end
1550 end
1551
1552 describe "fetch_follow_information_for_user" do
1553 test "syncronizes following/followers counters" do
1554 user =
1555 insert(:user,
1556 local: false,
1557 follower_address: "http://localhost:4001/users/fuser2/followers",
1558 following_address: "http://localhost:4001/users/fuser2/following"
1559 )
1560
1561 {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
1562 assert info.follower_count == 527
1563 assert info.following_count == 267
1564 end
1565
1566 test "detects hidden followers" do
1567 mock(fn env ->
1568 case env.url do
1569 "http://localhost:4001/users/masto_closed/followers?page=1" ->
1570 %Tesla.Env{status: 403, body: ""}
1571
1572 _ ->
1573 apply(HttpRequestMock, :request, [env])
1574 end
1575 end)
1576
1577 user =
1578 insert(:user,
1579 local: false,
1580 follower_address: "http://localhost:4001/users/masto_closed/followers",
1581 following_address: "http://localhost:4001/users/masto_closed/following"
1582 )
1583
1584 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1585 assert follow_info.hide_followers == true
1586 assert follow_info.hide_follows == false
1587 end
1588
1589 test "detects hidden follows" do
1590 mock(fn env ->
1591 case env.url do
1592 "http://localhost:4001/users/masto_closed/following?page=1" ->
1593 %Tesla.Env{status: 403, body: ""}
1594
1595 _ ->
1596 apply(HttpRequestMock, :request, [env])
1597 end
1598 end)
1599
1600 user =
1601 insert(:user,
1602 local: false,
1603 follower_address: "http://localhost:4001/users/masto_closed/followers",
1604 following_address: "http://localhost:4001/users/masto_closed/following"
1605 )
1606
1607 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1608 assert follow_info.hide_followers == false
1609 assert follow_info.hide_follows == true
1610 end
1611
1612 test "detects hidden follows/followers for friendica" do
1613 user =
1614 insert(:user,
1615 local: false,
1616 follower_address: "http://localhost:8080/followers/fuser3",
1617 following_address: "http://localhost:8080/following/fuser3"
1618 )
1619
1620 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1621 assert follow_info.hide_followers == true
1622 assert follow_info.follower_count == 296
1623 assert follow_info.following_count == 32
1624 assert follow_info.hide_follows == true
1625 end
1626 end
1627
1628 describe "fetch_favourites/3" do
1629 test "returns a favourite activities sorted by adds to favorite" do
1630 user = insert(:user)
1631 other_user = insert(:user)
1632 user1 = insert(:user)
1633 user2 = insert(:user)
1634 {:ok, a1} = CommonAPI.post(user1, %{"status" => "bla"})
1635 {:ok, _a2} = CommonAPI.post(user2, %{"status" => "traps are happy"})
1636 {:ok, a3} = CommonAPI.post(user2, %{"status" => "Trees Are "})
1637 {:ok, a4} = CommonAPI.post(user2, %{"status" => "Agent Smith "})
1638 {:ok, a5} = CommonAPI.post(user1, %{"status" => "Red or Blue "})
1639
1640 {:ok, _, _} = CommonAPI.favorite(a4.id, user)
1641 {:ok, _, _} = CommonAPI.favorite(a3.id, other_user)
1642 Process.sleep(1000)
1643 {:ok, _, _} = CommonAPI.favorite(a3.id, user)
1644 {:ok, _, _} = CommonAPI.favorite(a5.id, other_user)
1645 Process.sleep(1000)
1646 {:ok, _, _} = CommonAPI.favorite(a5.id, user)
1647 {:ok, _, _} = CommonAPI.favorite(a4.id, other_user)
1648 Process.sleep(1000)
1649 {:ok, _, _} = CommonAPI.favorite(a1.id, user)
1650 {:ok, _, _} = CommonAPI.favorite(a1.id, other_user)
1651 result = ActivityPub.fetch_favourites(user)
1652
1653 assert Enum.map(result, & &1.id) == [a1.id, a5.id, a3.id, a4.id]
1654
1655 result = ActivityPub.fetch_favourites(user, %{"limit" => 2})
1656 assert Enum.map(result, & &1.id) == [a1.id, a5.id]
1657 end
1658 end
1659
1660 describe "Move activity" do
1661 test "create" do
1662 %{ap_id: old_ap_id} = old_user = insert(:user)
1663 %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
1664 follower = insert(:user)
1665 follower_move_opted_out = insert(:user, allow_following_move: false)
1666
1667 User.follow(follower, old_user)
1668 User.follow(follower_move_opted_out, old_user)
1669
1670 assert User.following?(follower, old_user)
1671 assert User.following?(follower_move_opted_out, old_user)
1672
1673 assert {:ok, activity} = ActivityPub.move(old_user, new_user)
1674
1675 assert %Activity{
1676 actor: ^old_ap_id,
1677 data: %{
1678 "actor" => ^old_ap_id,
1679 "object" => ^old_ap_id,
1680 "target" => ^new_ap_id,
1681 "type" => "Move"
1682 },
1683 local: true
1684 } = activity
1685
1686 params = %{
1687 "op" => "move_following",
1688 "origin_id" => old_user.id,
1689 "target_id" => new_user.id
1690 }
1691
1692 assert_enqueued(worker: Pleroma.Workers.BackgroundWorker, args: params)
1693
1694 Pleroma.Workers.BackgroundWorker.perform(params, nil)
1695
1696 refute User.following?(follower, old_user)
1697 assert User.following?(follower, new_user)
1698
1699 assert User.following?(follower_move_opted_out, old_user)
1700 refute User.following?(follower_move_opted_out, new_user)
1701
1702 activity = %Activity{activity | object: nil}
1703
1704 assert [%Notification{activity: ^activity}] =
1705 Notification.for_user(follower, %{with_move: true})
1706
1707 assert [%Notification{activity: ^activity}] =
1708 Notification.for_user(follower_move_opted_out, %{with_move: true})
1709 end
1710
1711 test "old user must be in the new user's `also_known_as` list" do
1712 old_user = insert(:user)
1713 new_user = insert(:user)
1714
1715 assert {:error, "Target account must have the origin in `alsoKnownAs`"} =
1716 ActivityPub.move(old_user, new_user)
1717 end
1718 end
1719 end