Merge remote-tracking branch 'remotes/upstream/develop' into 1335-user-api-id-fields...
[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 end
1263
1264 describe "timeline post-processing" do
1265 test "it filters broken threads" do
1266 user1 = insert(:user)
1267 user2 = insert(:user)
1268 user3 = insert(:user)
1269
1270 {:ok, user1} = User.follow(user1, user3)
1271 assert User.following?(user1, user3)
1272
1273 {:ok, user2} = User.follow(user2, user3)
1274 assert User.following?(user2, user3)
1275
1276 {:ok, user3} = User.follow(user3, user2)
1277 assert User.following?(user3, user2)
1278
1279 {:ok, public_activity} = CommonAPI.post(user3, %{"status" => "hi 1"})
1280
1281 {:ok, private_activity_1} =
1282 CommonAPI.post(user3, %{"status" => "hi 2", "visibility" => "private"})
1283
1284 {:ok, private_activity_2} =
1285 CommonAPI.post(user2, %{
1286 "status" => "hi 3",
1287 "visibility" => "private",
1288 "in_reply_to_status_id" => private_activity_1.id
1289 })
1290
1291 {:ok, private_activity_3} =
1292 CommonAPI.post(user3, %{
1293 "status" => "hi 4",
1294 "visibility" => "private",
1295 "in_reply_to_status_id" => private_activity_2.id
1296 })
1297
1298 activities =
1299 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)])
1300 |> Enum.map(fn a -> a.id end)
1301
1302 private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"])
1303
1304 assert [public_activity.id, private_activity_1.id, private_activity_3.id] == activities
1305
1306 assert length(activities) == 3
1307
1308 activities =
1309 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)], %{"user" => user1})
1310 |> Enum.map(fn a -> a.id end)
1311
1312 assert [public_activity.id, private_activity_1.id] == activities
1313 assert length(activities) == 2
1314 end
1315 end
1316
1317 describe "update" do
1318 test "it creates an update activity with the new user data" do
1319 user = insert(:user)
1320 {:ok, user} = User.ensure_keys_present(user)
1321 user_data = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
1322
1323 {:ok, update} =
1324 ActivityPub.update(%{
1325 actor: user_data["id"],
1326 to: [user.follower_address],
1327 cc: [],
1328 object: user_data
1329 })
1330
1331 assert update.data["actor"] == user.ap_id
1332 assert update.data["to"] == [user.follower_address]
1333 assert embedded_object = update.data["object"]
1334 assert embedded_object["id"] == user_data["id"]
1335 assert embedded_object["type"] == user_data["type"]
1336 end
1337 end
1338
1339 test "returned pinned statuses" do
1340 Pleroma.Config.put([:instance, :max_pinned_statuses], 3)
1341 user = insert(:user)
1342
1343 {:ok, activity_one} = CommonAPI.post(user, %{"status" => "HI!!!"})
1344 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
1345 {:ok, activity_three} = CommonAPI.post(user, %{"status" => "HI!!!"})
1346
1347 CommonAPI.pin(activity_one.id, user)
1348 user = refresh_record(user)
1349
1350 CommonAPI.pin(activity_two.id, user)
1351 user = refresh_record(user)
1352
1353 CommonAPI.pin(activity_three.id, user)
1354 user = refresh_record(user)
1355
1356 activities = ActivityPub.fetch_user_activities(user, nil, %{"pinned" => "true"})
1357
1358 assert 3 = length(activities)
1359 end
1360
1361 describe "flag/1" do
1362 setup do
1363 reporter = insert(:user)
1364 target_account = insert(:user)
1365 content = "foobar"
1366 {:ok, activity} = CommonAPI.post(target_account, %{"status" => content})
1367 context = Utils.generate_context_id()
1368
1369 reporter_ap_id = reporter.ap_id
1370 target_ap_id = target_account.ap_id
1371 activity_ap_id = activity.data["id"]
1372
1373 activity_with_object = Activity.get_by_ap_id_with_object(activity_ap_id)
1374
1375 {:ok,
1376 %{
1377 reporter: reporter,
1378 context: context,
1379 target_account: target_account,
1380 reported_activity: activity,
1381 content: content,
1382 activity_ap_id: activity_ap_id,
1383 activity_with_object: activity_with_object,
1384 reporter_ap_id: reporter_ap_id,
1385 target_ap_id: target_ap_id
1386 }}
1387 end
1388
1389 test "it can create a Flag activity",
1390 %{
1391 reporter: reporter,
1392 context: context,
1393 target_account: target_account,
1394 reported_activity: reported_activity,
1395 content: content,
1396 activity_ap_id: activity_ap_id,
1397 activity_with_object: activity_with_object,
1398 reporter_ap_id: reporter_ap_id,
1399 target_ap_id: target_ap_id
1400 } do
1401 assert {:ok, activity} =
1402 ActivityPub.flag(%{
1403 actor: reporter,
1404 context: context,
1405 account: target_account,
1406 statuses: [reported_activity],
1407 content: content
1408 })
1409
1410 note_obj = %{
1411 "type" => "Note",
1412 "id" => activity_ap_id,
1413 "content" => content,
1414 "published" => activity_with_object.object.data["published"],
1415 "actor" => AccountView.render("show.json", %{user: target_account})
1416 }
1417
1418 assert %Activity{
1419 actor: ^reporter_ap_id,
1420 data: %{
1421 "type" => "Flag",
1422 "content" => ^content,
1423 "context" => ^context,
1424 "object" => [^target_ap_id, ^note_obj]
1425 }
1426 } = activity
1427 end
1428
1429 test_with_mock "strips status data from Flag, before federating it",
1430 %{
1431 reporter: reporter,
1432 context: context,
1433 target_account: target_account,
1434 reported_activity: reported_activity,
1435 content: content
1436 },
1437 Utils,
1438 [:passthrough],
1439 [] do
1440 {:ok, activity} =
1441 ActivityPub.flag(%{
1442 actor: reporter,
1443 context: context,
1444 account: target_account,
1445 statuses: [reported_activity],
1446 content: content
1447 })
1448
1449 new_data =
1450 put_in(activity.data, ["object"], [target_account.ap_id, reported_activity.data["id"]])
1451
1452 assert_called(Utils.maybe_federate(%{activity | data: new_data}))
1453 end
1454 end
1455
1456 test "fetch_activities/2 returns activities addressed to a list " do
1457 user = insert(:user)
1458 member = insert(:user)
1459 {:ok, list} = Pleroma.List.create("foo", user)
1460 {:ok, list} = Pleroma.List.follow(list, member)
1461
1462 {:ok, activity} =
1463 CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"})
1464
1465 activity = Repo.preload(activity, :bookmark)
1466 activity = %Activity{activity | thread_muted?: !!activity.thread_muted?}
1467
1468 assert ActivityPub.fetch_activities([], %{"user" => user}) == [activity]
1469 end
1470
1471 def data_uri do
1472 File.read!("test/fixtures/avatar_data_uri")
1473 end
1474
1475 describe "fetch_activities_bounded" do
1476 test "fetches private posts for followed users" do
1477 user = insert(:user)
1478
1479 {:ok, activity} =
1480 CommonAPI.post(user, %{
1481 "status" => "thought I looked cute might delete later :3",
1482 "visibility" => "private"
1483 })
1484
1485 [result] = ActivityPub.fetch_activities_bounded([user.follower_address], [])
1486 assert result.id == activity.id
1487 end
1488
1489 test "fetches only public posts for other users" do
1490 user = insert(:user)
1491 {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe", "visibility" => "public"})
1492
1493 {:ok, _private_activity} =
1494 CommonAPI.post(user, %{
1495 "status" => "why is tenshi eating a corndog so cute?",
1496 "visibility" => "private"
1497 })
1498
1499 [result] = ActivityPub.fetch_activities_bounded([], [user.follower_address])
1500 assert result.id == activity.id
1501 end
1502 end
1503
1504 describe "fetch_follow_information_for_user" do
1505 test "syncronizes following/followers counters" do
1506 user =
1507 insert(:user,
1508 local: false,
1509 follower_address: "http://localhost:4001/users/fuser2/followers",
1510 following_address: "http://localhost:4001/users/fuser2/following"
1511 )
1512
1513 {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
1514 assert info.follower_count == 527
1515 assert info.following_count == 267
1516 end
1517
1518 test "detects hidden followers" do
1519 mock(fn env ->
1520 case env.url do
1521 "http://localhost:4001/users/masto_closed/followers?page=1" ->
1522 %Tesla.Env{status: 403, body: ""}
1523
1524 _ ->
1525 apply(HttpRequestMock, :request, [env])
1526 end
1527 end)
1528
1529 user =
1530 insert(:user,
1531 local: false,
1532 follower_address: "http://localhost:4001/users/masto_closed/followers",
1533 following_address: "http://localhost:4001/users/masto_closed/following"
1534 )
1535
1536 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1537 assert follow_info.hide_followers == true
1538 assert follow_info.hide_follows == false
1539 end
1540
1541 test "detects hidden follows" do
1542 mock(fn env ->
1543 case env.url do
1544 "http://localhost:4001/users/masto_closed/following?page=1" ->
1545 %Tesla.Env{status: 403, body: ""}
1546
1547 _ ->
1548 apply(HttpRequestMock, :request, [env])
1549 end
1550 end)
1551
1552 user =
1553 insert(:user,
1554 local: false,
1555 follower_address: "http://localhost:4001/users/masto_closed/followers",
1556 following_address: "http://localhost:4001/users/masto_closed/following"
1557 )
1558
1559 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1560 assert follow_info.hide_followers == false
1561 assert follow_info.hide_follows == true
1562 end
1563
1564 test "detects hidden follows/followers for friendica" do
1565 user =
1566 insert(:user,
1567 local: false,
1568 follower_address: "http://localhost:8080/followers/fuser3",
1569 following_address: "http://localhost:8080/following/fuser3"
1570 )
1571
1572 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1573 assert follow_info.hide_followers == true
1574 assert follow_info.follower_count == 296
1575 assert follow_info.following_count == 32
1576 assert follow_info.hide_follows == true
1577 end
1578 end
1579
1580 describe "Move activity" do
1581 test "create" do
1582 %{ap_id: old_ap_id} = old_user = insert(:user)
1583 %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
1584 follower = insert(:user)
1585 follower_move_opted_out = insert(:user, allow_following_move: false)
1586
1587 User.follow(follower, old_user)
1588 User.follow(follower_move_opted_out, old_user)
1589
1590 assert User.following?(follower, old_user)
1591 assert User.following?(follower_move_opted_out, old_user)
1592
1593 assert {:ok, activity} = ActivityPub.move(old_user, new_user)
1594
1595 assert %Activity{
1596 actor: ^old_ap_id,
1597 data: %{
1598 "actor" => ^old_ap_id,
1599 "object" => ^old_ap_id,
1600 "target" => ^new_ap_id,
1601 "type" => "Move"
1602 },
1603 local: true
1604 } = activity
1605
1606 params = %{
1607 "op" => "move_following",
1608 "origin_id" => old_user.id,
1609 "target_id" => new_user.id
1610 }
1611
1612 assert_enqueued(worker: Pleroma.Workers.BackgroundWorker, args: params)
1613
1614 Pleroma.Workers.BackgroundWorker.perform(params, nil)
1615
1616 refute User.following?(follower, old_user)
1617 assert User.following?(follower, new_user)
1618
1619 assert User.following?(follower_move_opted_out, old_user)
1620 refute User.following?(follower_move_opted_out, new_user)
1621
1622 activity = %Activity{activity | object: nil}
1623
1624 assert [%Notification{activity: ^activity}] =
1625 Notification.for_user_since(follower, ~N[2019-04-13 11:22:33])
1626
1627 assert [%Notification{activity: ^activity}] =
1628 Notification.for_user_since(follower_move_opted_out, ~N[2019-04-13 11:22:33])
1629 end
1630
1631 test "old user must be in the new user's `also_known_as` list" do
1632 old_user = insert(:user)
1633 new_user = insert(:user)
1634
1635 assert {:error, "Target account must have the origin in `alsoKnownAs`"} =
1636 ActivityPub.move(old_user, new_user)
1637 end
1638 end
1639 end