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