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