23dffb1509db126094d148c3fd8aabd9afd4ae8a
[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} = 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} = 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} = 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} = 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, blocker} = 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, blocker} = 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 "doesn't return muted activities" do
612 activity_one = insert(:note_activity)
613 activity_two = insert(:note_activity)
614 activity_three = insert(:note_activity)
615 user = insert(:user)
616 booster = insert(:user)
617 {:ok, user} = User.mute(user, %User{ap_id: activity_one.data["actor"]})
618
619 activities =
620 ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
621
622 assert Enum.member?(activities, activity_two)
623 assert Enum.member?(activities, activity_three)
624 refute Enum.member?(activities, activity_one)
625
626 # Calling with 'with_muted' will deliver muted activities, too.
627 activities =
628 ActivityPub.fetch_activities([], %{
629 "muting_user" => user,
630 "with_muted" => true,
631 "skip_preload" => true
632 })
633
634 assert Enum.member?(activities, activity_two)
635 assert Enum.member?(activities, activity_three)
636 assert Enum.member?(activities, activity_one)
637
638 {:ok, user} = User.unmute(user, %User{ap_id: activity_one.data["actor"]})
639
640 activities =
641 ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
642
643 assert Enum.member?(activities, activity_two)
644 assert Enum.member?(activities, activity_three)
645 assert Enum.member?(activities, activity_one)
646
647 {:ok, user} = User.mute(user, %User{ap_id: activity_three.data["actor"]})
648 {:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
649 %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
650 activity_three = Activity.get_by_id(activity_three.id)
651
652 activities =
653 ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
654
655 assert Enum.member?(activities, activity_two)
656 refute Enum.member?(activities, activity_three)
657 refute Enum.member?(activities, boost_activity)
658 assert Enum.member?(activities, activity_one)
659
660 activities = ActivityPub.fetch_activities([], %{"muting_user" => nil, "skip_preload" => true})
661
662 assert Enum.member?(activities, activity_two)
663 assert Enum.member?(activities, activity_three)
664 assert Enum.member?(activities, boost_activity)
665 assert Enum.member?(activities, activity_one)
666 end
667
668 test "doesn't return thread muted activities" do
669 user = insert(:user)
670 _activity_one = insert(:note_activity)
671 note_two = insert(:note, data: %{"context" => "suya.."})
672 activity_two = insert(:note_activity, note: note_two)
673
674 {:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
675
676 assert [_activity_one] = ActivityPub.fetch_activities([], %{"muting_user" => user})
677 end
678
679 test "returns thread muted activities when with_muted is set" do
680 user = insert(:user)
681 _activity_one = insert(:note_activity)
682 note_two = insert(:note, data: %{"context" => "suya.."})
683 activity_two = insert(:note_activity, note: note_two)
684
685 {:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
686
687 assert [_activity_two, _activity_one] =
688 ActivityPub.fetch_activities([], %{"muting_user" => user, "with_muted" => true})
689 end
690
691 test "does include announces on request" do
692 activity_three = insert(:note_activity)
693 user = insert(:user)
694 booster = insert(:user)
695
696 {:ok, user} = User.follow(user, booster)
697
698 {:ok, announce, _object} = CommonAPI.repeat(activity_three.id, booster)
699
700 [announce_activity] = ActivityPub.fetch_activities([user.ap_id | User.following(user)])
701
702 assert announce_activity.id == announce.id
703 end
704
705 test "excludes reblogs on request" do
706 user = insert(:user)
707 {:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Create"}, %{:user => user})
708 {:ok, _} = ActivityBuilder.insert(%{"type" => "Announce"}, %{:user => user})
709
710 [activity] = ActivityPub.fetch_user_activities(user, nil, %{"exclude_reblogs" => "true"})
711
712 assert activity == expected_activity
713 end
714
715 describe "public fetch activities" do
716 test "doesn't retrieve unlisted activities" do
717 user = insert(:user)
718
719 {:ok, _unlisted_activity} =
720 CommonAPI.post(user, %{"status" => "yeah", "visibility" => "unlisted"})
721
722 {:ok, listed_activity} = CommonAPI.post(user, %{"status" => "yeah"})
723
724 [activity] = ActivityPub.fetch_public_activities()
725
726 assert activity == listed_activity
727 end
728
729 test "retrieves public activities" do
730 _activities = ActivityPub.fetch_public_activities()
731
732 %{public: public} = ActivityBuilder.public_and_non_public()
733
734 activities = ActivityPub.fetch_public_activities()
735 assert length(activities) == 1
736 assert Enum.at(activities, 0) == public
737 end
738
739 test "retrieves a maximum of 20 activities" do
740 ActivityBuilder.insert_list(10)
741 expected_activities = ActivityBuilder.insert_list(20)
742
743 activities = ActivityPub.fetch_public_activities()
744
745 assert collect_ids(activities) == collect_ids(expected_activities)
746 assert length(activities) == 20
747 end
748
749 test "retrieves ids starting from a since_id" do
750 activities = ActivityBuilder.insert_list(30)
751 expected_activities = ActivityBuilder.insert_list(10)
752 since_id = List.last(activities).id
753
754 activities = ActivityPub.fetch_public_activities(%{"since_id" => since_id})
755
756 assert collect_ids(activities) == collect_ids(expected_activities)
757 assert length(activities) == 10
758 end
759
760 test "retrieves ids up to max_id" do
761 ActivityBuilder.insert_list(10)
762 expected_activities = ActivityBuilder.insert_list(20)
763
764 %{id: max_id} =
765 10
766 |> ActivityBuilder.insert_list()
767 |> List.first()
768
769 activities = ActivityPub.fetch_public_activities(%{"max_id" => max_id})
770
771 assert length(activities) == 20
772 assert collect_ids(activities) == collect_ids(expected_activities)
773 end
774
775 test "paginates via offset/limit" do
776 _first_part_activities = ActivityBuilder.insert_list(10)
777 second_part_activities = ActivityBuilder.insert_list(10)
778
779 later_activities = ActivityBuilder.insert_list(10)
780
781 activities =
782 ActivityPub.fetch_public_activities(%{"page" => "2", "page_size" => "20"}, :offset)
783
784 assert length(activities) == 20
785
786 assert collect_ids(activities) ==
787 collect_ids(second_part_activities) ++ collect_ids(later_activities)
788 end
789
790 test "doesn't return reblogs for users for whom reblogs have been muted" do
791 activity = insert(:note_activity)
792 user = insert(:user)
793 booster = insert(:user)
794 {:ok, user} = CommonAPI.hide_reblogs(user, booster)
795
796 {:ok, activity, _} = CommonAPI.repeat(activity.id, booster)
797
798 activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
799
800 refute Enum.any?(activities, fn %{id: id} -> id == activity.id end)
801 end
802
803 test "returns reblogs for users for whom reblogs have not been muted" do
804 activity = insert(:note_activity)
805 user = insert(:user)
806 booster = insert(:user)
807 {:ok, user} = CommonAPI.hide_reblogs(user, booster)
808 {:ok, user} = CommonAPI.show_reblogs(user, booster)
809
810 {:ok, activity, _} = CommonAPI.repeat(activity.id, booster)
811
812 activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
813
814 assert Enum.any?(activities, fn %{id: id} -> id == activity.id end)
815 end
816 end
817
818 describe "react to an object" do
819 test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
820 Pleroma.Config.put([:instance, :federating], true)
821 user = insert(:user)
822 reactor = insert(:user)
823 {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
824 assert object = Object.normalize(activity)
825
826 {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥")
827
828 assert called(Pleroma.Web.Federator.publish(reaction_activity))
829 end
830
831 test "adds an emoji reaction activity to the db" do
832 user = insert(:user)
833 reactor = insert(:user)
834 {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
835 assert object = Object.normalize(activity)
836
837 {:ok, reaction_activity, object} = ActivityPub.react_with_emoji(reactor, object, "🔥")
838
839 assert reaction_activity
840
841 assert reaction_activity.data["actor"] == reactor.ap_id
842 assert reaction_activity.data["type"] == "EmojiReaction"
843 assert reaction_activity.data["content"] == "🔥"
844 assert reaction_activity.data["object"] == object.data["id"]
845 assert reaction_activity.data["to"] == [User.ap_followers(reactor), activity.data["actor"]]
846 assert reaction_activity.data["context"] == object.data["context"]
847 assert object.data["reaction_count"] == 1
848 assert object.data["reactions"]["🔥"] == [reactor.ap_id]
849 end
850 end
851
852 describe "unreacting to an object" do
853 test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
854 Pleroma.Config.put([:instance, :federating], true)
855 user = insert(:user)
856 reactor = insert(:user)
857 {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
858 assert object = Object.normalize(activity)
859
860 {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥")
861
862 assert called(Pleroma.Web.Federator.publish(reaction_activity))
863
864 {:ok, unreaction_activity, _object} =
865 ActivityPub.unreact_with_emoji(reactor, reaction_activity.data["id"])
866
867 assert called(Pleroma.Web.Federator.publish(unreaction_activity))
868 end
869
870 test "adds an undo activity to the db" do
871 user = insert(:user)
872 reactor = insert(:user)
873 {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
874 assert object = Object.normalize(activity)
875
876 {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥")
877
878 {:ok, unreaction_activity, _object} =
879 ActivityPub.unreact_with_emoji(reactor, reaction_activity.data["id"])
880
881 assert unreaction_activity.actor == reactor.ap_id
882 assert unreaction_activity.data["object"] == reaction_activity.data["id"]
883
884 object = Object.get_by_ap_id(object.data["id"])
885 assert object.data["reaction_count"] == 0
886 assert object.data["reactions"] == %{}
887 end
888 end
889
890 describe "like an object" do
891 test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
892 Pleroma.Config.put([:instance, :federating], true)
893 note_activity = insert(:note_activity)
894 assert object_activity = Object.normalize(note_activity)
895
896 user = insert(:user)
897
898 {:ok, like_activity, _object} = ActivityPub.like(user, object_activity)
899 assert called(Pleroma.Web.Federator.publish(like_activity))
900 end
901
902 test "returns exist activity if object already liked" do
903 note_activity = insert(:note_activity)
904 assert object_activity = Object.normalize(note_activity)
905
906 user = insert(:user)
907
908 {:ok, like_activity, _object} = ActivityPub.like(user, object_activity)
909
910 {:ok, like_activity_exist, _object} = ActivityPub.like(user, object_activity)
911 assert like_activity == like_activity_exist
912 end
913
914 test "adds a like activity to the db" do
915 note_activity = insert(:note_activity)
916 assert object = Object.normalize(note_activity)
917
918 user = insert(:user)
919 user_two = insert(:user)
920
921 {:ok, like_activity, object} = ActivityPub.like(user, object)
922
923 assert like_activity.data["actor"] == user.ap_id
924 assert like_activity.data["type"] == "Like"
925 assert like_activity.data["object"] == object.data["id"]
926 assert like_activity.data["to"] == [User.ap_followers(user), note_activity.data["actor"]]
927 assert like_activity.data["context"] == object.data["context"]
928 assert object.data["like_count"] == 1
929 assert object.data["likes"] == [user.ap_id]
930
931 # Just return the original activity if the user already liked it.
932 {:ok, same_like_activity, object} = ActivityPub.like(user, object)
933
934 assert like_activity == same_like_activity
935 assert object.data["likes"] == [user.ap_id]
936 assert object.data["like_count"] == 1
937
938 {:ok, _like_activity, object} = ActivityPub.like(user_two, object)
939 assert object.data["like_count"] == 2
940 end
941 end
942
943 describe "unliking" do
944 test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
945 Pleroma.Config.put([:instance, :federating], true)
946
947 note_activity = insert(:note_activity)
948 object = Object.normalize(note_activity)
949 user = insert(:user)
950
951 {:ok, object} = ActivityPub.unlike(user, object)
952 refute called(Pleroma.Web.Federator.publish())
953
954 {:ok, _like_activity, object} = ActivityPub.like(user, object)
955 assert object.data["like_count"] == 1
956
957 {:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object)
958 assert object.data["like_count"] == 0
959
960 assert called(Pleroma.Web.Federator.publish(unlike_activity))
961 end
962
963 test "unliking a previously liked object" do
964 note_activity = insert(:note_activity)
965 object = Object.normalize(note_activity)
966 user = insert(:user)
967
968 # Unliking something that hasn't been liked does nothing
969 {:ok, object} = ActivityPub.unlike(user, object)
970 assert object.data["like_count"] == 0
971
972 {:ok, like_activity, object} = ActivityPub.like(user, object)
973 assert object.data["like_count"] == 1
974
975 {:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object)
976 assert object.data["like_count"] == 0
977
978 assert Activity.get_by_id(like_activity.id) == nil
979 assert note_activity.actor in unlike_activity.recipients
980 end
981 end
982
983 describe "announcing an object" do
984 test "adds an announce activity to the db" do
985 note_activity = insert(:note_activity)
986 object = Object.normalize(note_activity)
987 user = insert(:user)
988
989 {:ok, announce_activity, object} = ActivityPub.announce(user, object)
990 assert object.data["announcement_count"] == 1
991 assert object.data["announcements"] == [user.ap_id]
992
993 assert announce_activity.data["to"] == [
994 User.ap_followers(user),
995 note_activity.data["actor"]
996 ]
997
998 assert announce_activity.data["object"] == object.data["id"]
999 assert announce_activity.data["actor"] == user.ap_id
1000 assert announce_activity.data["context"] == object.data["context"]
1001 end
1002 end
1003
1004 describe "announcing a private object" do
1005 test "adds an announce activity to the db if the audience is not widened" do
1006 user = insert(:user)
1007 {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
1008 object = Object.normalize(note_activity)
1009
1010 {:ok, announce_activity, object} = ActivityPub.announce(user, object, nil, true, false)
1011
1012 assert announce_activity.data["to"] == [User.ap_followers(user)]
1013
1014 assert announce_activity.data["object"] == object.data["id"]
1015 assert announce_activity.data["actor"] == user.ap_id
1016 assert announce_activity.data["context"] == object.data["context"]
1017 end
1018
1019 test "does not add an announce activity to the db if the audience is widened" do
1020 user = insert(:user)
1021 {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
1022 object = Object.normalize(note_activity)
1023
1024 assert {:error, _} = ActivityPub.announce(user, object, nil, true, true)
1025 end
1026
1027 test "does not add an announce activity to the db if the announcer is not the author" do
1028 user = insert(:user)
1029 announcer = insert(:user)
1030 {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
1031 object = Object.normalize(note_activity)
1032
1033 assert {:error, _} = ActivityPub.announce(announcer, object, nil, true, false)
1034 end
1035 end
1036
1037 describe "unannouncing an object" do
1038 test "unannouncing a previously announced object" do
1039 note_activity = insert(:note_activity)
1040 object = Object.normalize(note_activity)
1041 user = insert(:user)
1042
1043 # Unannouncing an object that is not announced does nothing
1044 # {:ok, object} = ActivityPub.unannounce(user, object)
1045 # assert object.data["announcement_count"] == 0
1046
1047 {:ok, announce_activity, object} = ActivityPub.announce(user, object)
1048 assert object.data["announcement_count"] == 1
1049
1050 {:ok, unannounce_activity, object} = ActivityPub.unannounce(user, object)
1051 assert object.data["announcement_count"] == 0
1052
1053 assert unannounce_activity.data["to"] == [
1054 User.ap_followers(user),
1055 object.data["actor"]
1056 ]
1057
1058 assert unannounce_activity.data["type"] == "Undo"
1059 assert unannounce_activity.data["object"] == announce_activity.data
1060 assert unannounce_activity.data["actor"] == user.ap_id
1061 assert unannounce_activity.data["context"] == announce_activity.data["context"]
1062
1063 assert Activity.get_by_id(announce_activity.id) == nil
1064 end
1065 end
1066
1067 describe "uploading files" do
1068 test "copies the file to the configured folder" do
1069 file = %Plug.Upload{
1070 content_type: "image/jpg",
1071 path: Path.absname("test/fixtures/image.jpg"),
1072 filename: "an_image.jpg"
1073 }
1074
1075 {:ok, %Object{} = object} = ActivityPub.upload(file)
1076 assert object.data["name"] == "an_image.jpg"
1077 end
1078
1079 test "works with base64 encoded images" do
1080 file = %{
1081 "img" => data_uri()
1082 }
1083
1084 {:ok, %Object{}} = ActivityPub.upload(file)
1085 end
1086 end
1087
1088 describe "fetch the latest Follow" do
1089 test "fetches the latest Follow activity" do
1090 %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
1091 follower = Repo.get_by(User, ap_id: activity.data["actor"])
1092 followed = Repo.get_by(User, ap_id: activity.data["object"])
1093
1094 assert activity == Utils.fetch_latest_follow(follower, followed)
1095 end
1096 end
1097
1098 describe "following / unfollowing" do
1099 test "creates a follow activity" do
1100 follower = insert(:user)
1101 followed = insert(:user)
1102
1103 {:ok, activity} = ActivityPub.follow(follower, followed)
1104 assert activity.data["type"] == "Follow"
1105 assert activity.data["actor"] == follower.ap_id
1106 assert activity.data["object"] == followed.ap_id
1107 end
1108
1109 test "creates an undo activity for the last follow" do
1110 follower = insert(:user)
1111 followed = insert(:user)
1112
1113 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
1114 {:ok, activity} = ActivityPub.unfollow(follower, followed)
1115
1116 assert activity.data["type"] == "Undo"
1117 assert activity.data["actor"] == follower.ap_id
1118
1119 embedded_object = activity.data["object"]
1120 assert is_map(embedded_object)
1121 assert embedded_object["type"] == "Follow"
1122 assert embedded_object["object"] == followed.ap_id
1123 assert embedded_object["id"] == follow_activity.data["id"]
1124 end
1125 end
1126
1127 describe "blocking / unblocking" do
1128 test "creates a block activity" do
1129 blocker = insert(:user)
1130 blocked = insert(:user)
1131
1132 {:ok, activity} = ActivityPub.block(blocker, blocked)
1133
1134 assert activity.data["type"] == "Block"
1135 assert activity.data["actor"] == blocker.ap_id
1136 assert activity.data["object"] == blocked.ap_id
1137 end
1138
1139 test "creates an undo activity for the last block" do
1140 blocker = insert(:user)
1141 blocked = insert(:user)
1142
1143 {:ok, block_activity} = ActivityPub.block(blocker, blocked)
1144 {:ok, activity} = ActivityPub.unblock(blocker, blocked)
1145
1146 assert activity.data["type"] == "Undo"
1147 assert activity.data["actor"] == blocker.ap_id
1148
1149 embedded_object = activity.data["object"]
1150 assert is_map(embedded_object)
1151 assert embedded_object["type"] == "Block"
1152 assert embedded_object["object"] == blocked.ap_id
1153 assert embedded_object["id"] == block_activity.data["id"]
1154 end
1155 end
1156
1157 describe "deletion" do
1158 test "it creates a delete activity and deletes the original object" do
1159 note = insert(:note_activity)
1160 object = Object.normalize(note)
1161 {:ok, delete} = ActivityPub.delete(object)
1162
1163 assert delete.data["type"] == "Delete"
1164 assert delete.data["actor"] == note.data["actor"]
1165 assert delete.data["object"] == object.data["id"]
1166
1167 assert Activity.get_by_id(delete.id) != nil
1168
1169 assert Repo.get(Object, object.id).data["type"] == "Tombstone"
1170 end
1171
1172 test "decrements user note count only for public activities" do
1173 user = insert(:user, note_count: 10)
1174
1175 {:ok, a1} =
1176 CommonAPI.post(User.get_cached_by_id(user.id), %{
1177 "status" => "yeah",
1178 "visibility" => "public"
1179 })
1180
1181 {:ok, a2} =
1182 CommonAPI.post(User.get_cached_by_id(user.id), %{
1183 "status" => "yeah",
1184 "visibility" => "unlisted"
1185 })
1186
1187 {:ok, a3} =
1188 CommonAPI.post(User.get_cached_by_id(user.id), %{
1189 "status" => "yeah",
1190 "visibility" => "private"
1191 })
1192
1193 {:ok, a4} =
1194 CommonAPI.post(User.get_cached_by_id(user.id), %{
1195 "status" => "yeah",
1196 "visibility" => "direct"
1197 })
1198
1199 {:ok, _} = Object.normalize(a1) |> ActivityPub.delete()
1200 {:ok, _} = Object.normalize(a2) |> ActivityPub.delete()
1201 {:ok, _} = Object.normalize(a3) |> ActivityPub.delete()
1202 {:ok, _} = Object.normalize(a4) |> ActivityPub.delete()
1203
1204 user = User.get_cached_by_id(user.id)
1205 assert user.note_count == 10
1206 end
1207
1208 test "it creates a delete activity and checks that it is also sent to users mentioned by the deleted object" do
1209 user = insert(:user)
1210 note = insert(:note_activity)
1211 object = Object.normalize(note)
1212
1213 {:ok, object} =
1214 object
1215 |> Object.change(%{
1216 data: %{
1217 "actor" => object.data["actor"],
1218 "id" => object.data["id"],
1219 "to" => [user.ap_id],
1220 "type" => "Note"
1221 }
1222 })
1223 |> Object.update_and_set_cache()
1224
1225 {:ok, delete} = ActivityPub.delete(object)
1226
1227 assert user.ap_id in delete.data["to"]
1228 end
1229
1230 test "decreases reply count" do
1231 user = insert(:user)
1232 user2 = insert(:user)
1233
1234 {:ok, activity} = CommonAPI.post(user, %{"status" => "1", "visibility" => "public"})
1235 reply_data = %{"status" => "1", "in_reply_to_status_id" => activity.id}
1236 ap_id = activity.data["id"]
1237
1238 {:ok, public_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "public"))
1239 {:ok, unlisted_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "unlisted"))
1240 {:ok, private_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "private"))
1241 {:ok, direct_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct"))
1242
1243 _ = CommonAPI.delete(direct_reply.id, user2)
1244 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1245 assert object.data["repliesCount"] == 2
1246
1247 _ = CommonAPI.delete(private_reply.id, user2)
1248 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1249 assert object.data["repliesCount"] == 2
1250
1251 _ = CommonAPI.delete(public_reply.id, user2)
1252 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1253 assert object.data["repliesCount"] == 1
1254
1255 _ = CommonAPI.delete(unlisted_reply.id, user2)
1256 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1257 assert object.data["repliesCount"] == 0
1258 end
1259
1260 test "it passes delete activity through MRF before deleting the object" do
1261 rewrite_policy = Pleroma.Config.get([:instance, :rewrite_policy])
1262 Pleroma.Config.put([:instance, :rewrite_policy], Pleroma.Web.ActivityPub.MRF.DropPolicy)
1263
1264 on_exit(fn -> Pleroma.Config.put([:instance, :rewrite_policy], rewrite_policy) end)
1265
1266 note = insert(:note_activity)
1267 object = Object.normalize(note)
1268
1269 {:error, {:reject, _}} = ActivityPub.delete(object)
1270
1271 assert Activity.get_by_id(note.id)
1272 assert Repo.get(Object, object.id).data["type"] == object.data["type"]
1273 end
1274 end
1275
1276 describe "timeline post-processing" do
1277 test "it filters broken threads" do
1278 user1 = insert(:user)
1279 user2 = insert(:user)
1280 user3 = insert(:user)
1281
1282 {:ok, user1} = User.follow(user1, user3)
1283 assert User.following?(user1, user3)
1284
1285 {:ok, user2} = User.follow(user2, user3)
1286 assert User.following?(user2, user3)
1287
1288 {:ok, user3} = User.follow(user3, user2)
1289 assert User.following?(user3, user2)
1290
1291 {:ok, public_activity} = CommonAPI.post(user3, %{"status" => "hi 1"})
1292
1293 {:ok, private_activity_1} =
1294 CommonAPI.post(user3, %{"status" => "hi 2", "visibility" => "private"})
1295
1296 {:ok, private_activity_2} =
1297 CommonAPI.post(user2, %{
1298 "status" => "hi 3",
1299 "visibility" => "private",
1300 "in_reply_to_status_id" => private_activity_1.id
1301 })
1302
1303 {:ok, private_activity_3} =
1304 CommonAPI.post(user3, %{
1305 "status" => "hi 4",
1306 "visibility" => "private",
1307 "in_reply_to_status_id" => private_activity_2.id
1308 })
1309
1310 activities =
1311 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)])
1312 |> Enum.map(fn a -> a.id end)
1313
1314 private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"])
1315
1316 assert [public_activity.id, private_activity_1.id, private_activity_3.id] == activities
1317
1318 assert length(activities) == 3
1319
1320 activities =
1321 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)], %{"user" => user1})
1322 |> Enum.map(fn a -> a.id end)
1323
1324 assert [public_activity.id, private_activity_1.id] == activities
1325 assert length(activities) == 2
1326 end
1327 end
1328
1329 describe "update" do
1330 test "it creates an update activity with the new user data" do
1331 user = insert(:user)
1332 {:ok, user} = User.ensure_keys_present(user)
1333 user_data = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
1334
1335 {:ok, update} =
1336 ActivityPub.update(%{
1337 actor: user_data["id"],
1338 to: [user.follower_address],
1339 cc: [],
1340 object: user_data
1341 })
1342
1343 assert update.data["actor"] == user.ap_id
1344 assert update.data["to"] == [user.follower_address]
1345 assert embedded_object = update.data["object"]
1346 assert embedded_object["id"] == user_data["id"]
1347 assert embedded_object["type"] == user_data["type"]
1348 end
1349 end
1350
1351 test "returned pinned statuses" do
1352 Pleroma.Config.put([:instance, :max_pinned_statuses], 3)
1353 user = insert(:user)
1354
1355 {:ok, activity_one} = CommonAPI.post(user, %{"status" => "HI!!!"})
1356 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
1357 {:ok, activity_three} = CommonAPI.post(user, %{"status" => "HI!!!"})
1358
1359 CommonAPI.pin(activity_one.id, user)
1360 user = refresh_record(user)
1361
1362 CommonAPI.pin(activity_two.id, user)
1363 user = refresh_record(user)
1364
1365 CommonAPI.pin(activity_three.id, user)
1366 user = refresh_record(user)
1367
1368 activities = ActivityPub.fetch_user_activities(user, nil, %{"pinned" => "true"})
1369
1370 assert 3 = length(activities)
1371 end
1372
1373 describe "flag/1" do
1374 setup do
1375 reporter = insert(:user)
1376 target_account = insert(:user)
1377 content = "foobar"
1378 {:ok, activity} = CommonAPI.post(target_account, %{"status" => content})
1379 context = Utils.generate_context_id()
1380
1381 reporter_ap_id = reporter.ap_id
1382 target_ap_id = target_account.ap_id
1383 activity_ap_id = activity.data["id"]
1384
1385 activity_with_object = Activity.get_by_ap_id_with_object(activity_ap_id)
1386
1387 {:ok,
1388 %{
1389 reporter: reporter,
1390 context: context,
1391 target_account: target_account,
1392 reported_activity: activity,
1393 content: content,
1394 activity_ap_id: activity_ap_id,
1395 activity_with_object: activity_with_object,
1396 reporter_ap_id: reporter_ap_id,
1397 target_ap_id: target_ap_id
1398 }}
1399 end
1400
1401 test "it can create a Flag activity",
1402 %{
1403 reporter: reporter,
1404 context: context,
1405 target_account: target_account,
1406 reported_activity: reported_activity,
1407 content: content,
1408 activity_ap_id: activity_ap_id,
1409 activity_with_object: activity_with_object,
1410 reporter_ap_id: reporter_ap_id,
1411 target_ap_id: target_ap_id
1412 } do
1413 assert {:ok, activity} =
1414 ActivityPub.flag(%{
1415 actor: reporter,
1416 context: context,
1417 account: target_account,
1418 statuses: [reported_activity],
1419 content: content
1420 })
1421
1422 note_obj = %{
1423 "type" => "Note",
1424 "id" => activity_ap_id,
1425 "content" => content,
1426 "published" => activity_with_object.object.data["published"],
1427 "actor" => AccountView.render("show.json", %{user: target_account})
1428 }
1429
1430 assert %Activity{
1431 actor: ^reporter_ap_id,
1432 data: %{
1433 "type" => "Flag",
1434 "content" => ^content,
1435 "context" => ^context,
1436 "object" => [^target_ap_id, ^note_obj]
1437 }
1438 } = activity
1439 end
1440
1441 test_with_mock "strips status data from Flag, before federating it",
1442 %{
1443 reporter: reporter,
1444 context: context,
1445 target_account: target_account,
1446 reported_activity: reported_activity,
1447 content: content
1448 },
1449 Utils,
1450 [:passthrough],
1451 [] do
1452 {:ok, activity} =
1453 ActivityPub.flag(%{
1454 actor: reporter,
1455 context: context,
1456 account: target_account,
1457 statuses: [reported_activity],
1458 content: content
1459 })
1460
1461 new_data =
1462 put_in(activity.data, ["object"], [target_account.ap_id, reported_activity.data["id"]])
1463
1464 assert_called(Utils.maybe_federate(%{activity | data: new_data}))
1465 end
1466 end
1467
1468 test "fetch_activities/2 returns activities addressed to a list " do
1469 user = insert(:user)
1470 member = insert(:user)
1471 {:ok, list} = Pleroma.List.create("foo", user)
1472 {:ok, list} = Pleroma.List.follow(list, member)
1473
1474 {:ok, activity} =
1475 CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"})
1476
1477 activity = Repo.preload(activity, :bookmark)
1478 activity = %Activity{activity | thread_muted?: !!activity.thread_muted?}
1479
1480 assert ActivityPub.fetch_activities([], %{"user" => user}) == [activity]
1481 end
1482
1483 def data_uri do
1484 File.read!("test/fixtures/avatar_data_uri")
1485 end
1486
1487 describe "fetch_activities_bounded" do
1488 test "fetches private posts for followed users" do
1489 user = insert(:user)
1490
1491 {:ok, activity} =
1492 CommonAPI.post(user, %{
1493 "status" => "thought I looked cute might delete later :3",
1494 "visibility" => "private"
1495 })
1496
1497 [result] = ActivityPub.fetch_activities_bounded([user.follower_address], [])
1498 assert result.id == activity.id
1499 end
1500
1501 test "fetches only public posts for other users" do
1502 user = insert(:user)
1503 {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe", "visibility" => "public"})
1504
1505 {:ok, _private_activity} =
1506 CommonAPI.post(user, %{
1507 "status" => "why is tenshi eating a corndog so cute?",
1508 "visibility" => "private"
1509 })
1510
1511 [result] = ActivityPub.fetch_activities_bounded([], [user.follower_address])
1512 assert result.id == activity.id
1513 end
1514 end
1515
1516 describe "fetch_follow_information_for_user" do
1517 test "syncronizes following/followers counters" do
1518 user =
1519 insert(:user,
1520 local: false,
1521 follower_address: "http://localhost:4001/users/fuser2/followers",
1522 following_address: "http://localhost:4001/users/fuser2/following"
1523 )
1524
1525 {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
1526 assert info.follower_count == 527
1527 assert info.following_count == 267
1528 end
1529
1530 test "detects hidden followers" do
1531 mock(fn env ->
1532 case env.url do
1533 "http://localhost:4001/users/masto_closed/followers?page=1" ->
1534 %Tesla.Env{status: 403, body: ""}
1535
1536 _ ->
1537 apply(HttpRequestMock, :request, [env])
1538 end
1539 end)
1540
1541 user =
1542 insert(:user,
1543 local: false,
1544 follower_address: "http://localhost:4001/users/masto_closed/followers",
1545 following_address: "http://localhost:4001/users/masto_closed/following"
1546 )
1547
1548 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1549 assert follow_info.hide_followers == true
1550 assert follow_info.hide_follows == false
1551 end
1552
1553 test "detects hidden follows" do
1554 mock(fn env ->
1555 case env.url do
1556 "http://localhost:4001/users/masto_closed/following?page=1" ->
1557 %Tesla.Env{status: 403, body: ""}
1558
1559 _ ->
1560 apply(HttpRequestMock, :request, [env])
1561 end
1562 end)
1563
1564 user =
1565 insert(:user,
1566 local: false,
1567 follower_address: "http://localhost:4001/users/masto_closed/followers",
1568 following_address: "http://localhost:4001/users/masto_closed/following"
1569 )
1570
1571 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1572 assert follow_info.hide_followers == false
1573 assert follow_info.hide_follows == true
1574 end
1575
1576 test "detects hidden follows/followers for friendica" do
1577 user =
1578 insert(:user,
1579 local: false,
1580 follower_address: "http://localhost:8080/followers/fuser3",
1581 following_address: "http://localhost:8080/following/fuser3"
1582 )
1583
1584 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1585 assert follow_info.hide_followers == true
1586 assert follow_info.follower_count == 296
1587 assert follow_info.following_count == 32
1588 assert follow_info.hide_follows == true
1589 end
1590 end
1591
1592 describe "Move activity" do
1593 test "create" do
1594 %{ap_id: old_ap_id} = old_user = insert(:user)
1595 %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
1596 follower = insert(:user)
1597 follower_move_opted_out = insert(:user, allow_following_move: false)
1598
1599 User.follow(follower, old_user)
1600 User.follow(follower_move_opted_out, old_user)
1601
1602 assert User.following?(follower, old_user)
1603 assert User.following?(follower_move_opted_out, old_user)
1604
1605 assert {:ok, activity} = ActivityPub.move(old_user, new_user)
1606
1607 assert %Activity{
1608 actor: ^old_ap_id,
1609 data: %{
1610 "actor" => ^old_ap_id,
1611 "object" => ^old_ap_id,
1612 "target" => ^new_ap_id,
1613 "type" => "Move"
1614 },
1615 local: true
1616 } = activity
1617
1618 params = %{
1619 "op" => "move_following",
1620 "origin_id" => old_user.id,
1621 "target_id" => new_user.id
1622 }
1623
1624 assert_enqueued(worker: Pleroma.Workers.BackgroundWorker, args: params)
1625
1626 Pleroma.Workers.BackgroundWorker.perform(params, nil)
1627
1628 refute User.following?(follower, old_user)
1629 assert User.following?(follower, new_user)
1630
1631 assert User.following?(follower_move_opted_out, old_user)
1632 refute User.following?(follower_move_opted_out, new_user)
1633
1634 activity = %Activity{activity | object: nil}
1635
1636 assert [%Notification{activity: ^activity}] =
1637 Notification.for_user_since(follower, ~N[2019-04-13 11:22:33])
1638
1639 assert [%Notification{activity: ^activity}] =
1640 Notification.for_user_since(follower_move_opted_out, ~N[2019-04-13 11:22:33])
1641 end
1642
1643 test "old user must be in the new user's `also_known_as` list" do
1644 old_user = insert(:user)
1645 new_user = insert(:user)
1646
1647 assert {:error, "Target account must have the origin in `alsoKnownAs`"} =
1648 ActivityPub.move(old_user, new_user)
1649 end
1650 end
1651 end