503bbf0db662370cccfbc27bffcc27ea14bf0c7d
[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 alias Pleroma.Activity
8 alias Pleroma.Builders.ActivityBuilder
9 alias Pleroma.Object
10 alias Pleroma.User
11 alias Pleroma.Web.ActivityPub.ActivityPub
12 alias Pleroma.Web.ActivityPub.Utils
13 alias Pleroma.Web.AdminAPI.AccountView
14 alias Pleroma.Web.CommonAPI
15
16 import Pleroma.Factory
17 import Tesla.Mock
18 import Mock
19
20 setup do
21 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
22 :ok
23 end
24
25 clear_config([:instance, :federating])
26
27 describe "streaming out participations" do
28 test "it streams them out" do
29 user = insert(:user)
30 {:ok, activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
31
32 {:ok, conversation} = Pleroma.Conversation.create_or_bump_for(activity)
33
34 participations =
35 conversation.participations
36 |> Repo.preload(:user)
37
38 with_mock Pleroma.Web.Streamer,
39 stream: fn _, _ -> nil end do
40 ActivityPub.stream_out_participations(conversation.participations)
41
42 assert called(Pleroma.Web.Streamer.stream("participation", participations))
43 end
44 end
45
46 test "streams them out on activity creation" do
47 user_one = insert(:user)
48 user_two = insert(:user)
49
50 with_mock Pleroma.Web.Streamer,
51 stream: fn _, _ -> nil end do
52 {:ok, activity} =
53 CommonAPI.post(user_one, %{
54 "status" => "@#{user_two.nickname}",
55 "visibility" => "direct"
56 })
57
58 conversation =
59 activity.data["context"]
60 |> Pleroma.Conversation.get_for_ap_id()
61 |> Repo.preload(participations: :user)
62
63 assert called(Pleroma.Web.Streamer.stream("participation", conversation.participations))
64 end
65 end
66 end
67
68 describe "fetching restricted by visibility" do
69 test "it restricts by the appropriate visibility" do
70 user = insert(:user)
71
72 {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
73
74 {:ok, direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
75
76 {:ok, unlisted_activity} =
77 CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"})
78
79 {:ok, private_activity} =
80 CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
81
82 activities =
83 ActivityPub.fetch_activities([], %{:visibility => "direct", "actor_id" => user.ap_id})
84
85 assert activities == [direct_activity]
86
87 activities =
88 ActivityPub.fetch_activities([], %{:visibility => "unlisted", "actor_id" => user.ap_id})
89
90 assert activities == [unlisted_activity]
91
92 activities =
93 ActivityPub.fetch_activities([], %{:visibility => "private", "actor_id" => user.ap_id})
94
95 assert activities == [private_activity]
96
97 activities =
98 ActivityPub.fetch_activities([], %{:visibility => "public", "actor_id" => user.ap_id})
99
100 assert activities == [public_activity]
101
102 activities =
103 ActivityPub.fetch_activities([], %{
104 :visibility => ~w[private public],
105 "actor_id" => user.ap_id
106 })
107
108 assert activities == [public_activity, private_activity]
109 end
110 end
111
112 describe "fetching excluded by visibility" do
113 test "it excludes by the appropriate visibility" do
114 user = insert(:user)
115
116 {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
117
118 {:ok, direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
119
120 {:ok, unlisted_activity} =
121 CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"})
122
123 {:ok, private_activity} =
124 CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
125
126 activities =
127 ActivityPub.fetch_activities([], %{
128 "exclude_visibilities" => "direct",
129 "actor_id" => user.ap_id
130 })
131
132 assert public_activity in activities
133 assert unlisted_activity in activities
134 assert private_activity in activities
135 refute direct_activity in activities
136
137 activities =
138 ActivityPub.fetch_activities([], %{
139 "exclude_visibilities" => "unlisted",
140 "actor_id" => user.ap_id
141 })
142
143 assert public_activity in activities
144 refute unlisted_activity in activities
145 assert private_activity in activities
146 assert direct_activity in activities
147
148 activities =
149 ActivityPub.fetch_activities([], %{
150 "exclude_visibilities" => "private",
151 "actor_id" => user.ap_id
152 })
153
154 assert public_activity in activities
155 assert unlisted_activity in activities
156 refute private_activity in activities
157 assert direct_activity in activities
158
159 activities =
160 ActivityPub.fetch_activities([], %{
161 "exclude_visibilities" => "public",
162 "actor_id" => user.ap_id
163 })
164
165 refute public_activity in activities
166 assert unlisted_activity in activities
167 assert private_activity in activities
168 assert direct_activity in activities
169 end
170 end
171
172 describe "building a user from his ap id" do
173 test "it returns a user" do
174 user_id = "http://mastodon.example.org/users/admin"
175 {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
176 assert user.ap_id == user_id
177 assert user.nickname == "admin@mastodon.example.org"
178 assert user.source_data
179 assert user.ap_enabled
180 assert user.follower_address == "http://mastodon.example.org/users/admin/followers"
181 end
182
183 test "it returns a user that is invisible" do
184 user_id = "http://mastodon.example.org/users/relay"
185 {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
186 assert User.invisible?(user)
187 end
188
189 test "it fetches the appropriate tag-restricted posts" do
190 user = insert(:user)
191
192 {:ok, status_one} = CommonAPI.post(user, %{"status" => ". #test"})
193 {:ok, status_two} = CommonAPI.post(user, %{"status" => ". #essais"})
194 {:ok, status_three} = CommonAPI.post(user, %{"status" => ". #test #reject"})
195
196 fetch_one = ActivityPub.fetch_activities([], %{"type" => "Create", "tag" => "test"})
197
198 fetch_two =
199 ActivityPub.fetch_activities([], %{"type" => "Create", "tag" => ["test", "essais"]})
200
201 fetch_three =
202 ActivityPub.fetch_activities([], %{
203 "type" => "Create",
204 "tag" => ["test", "essais"],
205 "tag_reject" => ["reject"]
206 })
207
208 fetch_four =
209 ActivityPub.fetch_activities([], %{
210 "type" => "Create",
211 "tag" => ["test"],
212 "tag_all" => ["test", "reject"]
213 })
214
215 assert fetch_one == [status_one, status_three]
216 assert fetch_two == [status_one, status_two, status_three]
217 assert fetch_three == [status_one, status_two]
218 assert fetch_four == [status_three]
219 end
220 end
221
222 describe "insertion" do
223 test "drops activities beyond a certain limit" do
224 limit = Pleroma.Config.get([:instance, :remote_limit])
225
226 random_text =
227 :crypto.strong_rand_bytes(limit + 1)
228 |> Base.encode64()
229 |> binary_part(0, limit + 1)
230
231 data = %{
232 "ok" => true,
233 "object" => %{
234 "content" => random_text
235 }
236 }
237
238 assert {:error, {:remote_limit_error, _}} = ActivityPub.insert(data)
239 end
240
241 test "doesn't drop activities with content being null" do
242 user = insert(:user)
243
244 data = %{
245 "actor" => user.ap_id,
246 "to" => [],
247 "object" => %{
248 "actor" => user.ap_id,
249 "to" => [],
250 "type" => "Note",
251 "content" => nil
252 }
253 }
254
255 assert {:ok, _} = ActivityPub.insert(data)
256 end
257
258 test "returns the activity if one with the same id is already in" do
259 activity = insert(:note_activity)
260 {:ok, new_activity} = ActivityPub.insert(activity.data)
261
262 assert activity.id == new_activity.id
263 end
264
265 test "inserts a given map into the activity database, giving it an id if it has none." do
266 user = insert(:user)
267
268 data = %{
269 "actor" => user.ap_id,
270 "to" => [],
271 "object" => %{
272 "actor" => user.ap_id,
273 "to" => [],
274 "type" => "Note",
275 "content" => "hey"
276 }
277 }
278
279 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
280 assert activity.data["ok"] == data["ok"]
281 assert is_binary(activity.data["id"])
282
283 given_id = "bla"
284
285 data = %{
286 "id" => given_id,
287 "actor" => user.ap_id,
288 "to" => [],
289 "context" => "blabla",
290 "object" => %{
291 "actor" => user.ap_id,
292 "to" => [],
293 "type" => "Note",
294 "content" => "hey"
295 }
296 }
297
298 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
299 assert activity.data["ok"] == data["ok"]
300 assert activity.data["id"] == given_id
301 assert activity.data["context"] == "blabla"
302 assert activity.data["context_id"]
303 end
304
305 test "adds a context when none is there" do
306 user = insert(:user)
307
308 data = %{
309 "actor" => user.ap_id,
310 "to" => [],
311 "object" => %{
312 "actor" => user.ap_id,
313 "to" => [],
314 "type" => "Note",
315 "content" => "hey"
316 }
317 }
318
319 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
320 object = Pleroma.Object.normalize(activity)
321
322 assert is_binary(activity.data["context"])
323 assert is_binary(object.data["context"])
324 assert activity.data["context_id"]
325 assert object.data["context_id"]
326 end
327
328 test "adds an id to a given object if it lacks one and is a note and inserts it to the object database" do
329 user = insert(:user)
330
331 data = %{
332 "actor" => user.ap_id,
333 "to" => [],
334 "object" => %{
335 "actor" => user.ap_id,
336 "to" => [],
337 "type" => "Note",
338 "content" => "hey"
339 }
340 }
341
342 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
343 assert object = Object.normalize(activity)
344 assert is_binary(object.data["id"])
345 end
346 end
347
348 describe "listen activities" do
349 test "does not increase user note count" do
350 user = insert(:user)
351
352 {:ok, activity} =
353 ActivityPub.listen(%{
354 to: ["https://www.w3.org/ns/activitystreams#Public"],
355 actor: user,
356 context: "",
357 object: %{
358 "actor" => user.ap_id,
359 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
360 "artist" => "lain",
361 "title" => "lain radio episode 1",
362 "length" => 180_000,
363 "type" => "Audio"
364 }
365 })
366
367 assert activity.actor == user.ap_id
368
369 user = User.get_cached_by_id(user.id)
370 assert user.note_count == 0
371 end
372
373 test "can be fetched into a timeline" do
374 _listen_activity_1 = insert(:listen)
375 _listen_activity_2 = insert(:listen)
376 _listen_activity_3 = insert(:listen)
377
378 timeline = ActivityPub.fetch_activities([], %{"type" => ["Listen"]})
379
380 assert length(timeline) == 3
381 end
382 end
383
384 describe "create activities" do
385 test "removes doubled 'to' recipients" do
386 user = insert(:user)
387
388 {:ok, activity} =
389 ActivityPub.create(%{
390 to: ["user1", "user1", "user2"],
391 actor: user,
392 context: "",
393 object: %{
394 "to" => ["user1", "user1", "user2"],
395 "type" => "Note",
396 "content" => "testing"
397 }
398 })
399
400 assert activity.data["to"] == ["user1", "user2"]
401 assert activity.actor == user.ap_id
402 assert activity.recipients == ["user1", "user2", user.ap_id]
403 end
404
405 test "increases user note count only for public activities" do
406 user = insert(:user)
407
408 {:ok, _} =
409 CommonAPI.post(User.get_cached_by_id(user.id), %{
410 "status" => "1",
411 "visibility" => "public"
412 })
413
414 {:ok, _} =
415 CommonAPI.post(User.get_cached_by_id(user.id), %{
416 "status" => "2",
417 "visibility" => "unlisted"
418 })
419
420 {:ok, _} =
421 CommonAPI.post(User.get_cached_by_id(user.id), %{
422 "status" => "2",
423 "visibility" => "private"
424 })
425
426 {:ok, _} =
427 CommonAPI.post(User.get_cached_by_id(user.id), %{
428 "status" => "3",
429 "visibility" => "direct"
430 })
431
432 user = User.get_cached_by_id(user.id)
433 assert user.note_count == 2
434 end
435
436 test "increases replies count" do
437 user = insert(:user)
438 user2 = insert(:user)
439
440 {:ok, activity} = CommonAPI.post(user, %{"status" => "1", "visibility" => "public"})
441 ap_id = activity.data["id"]
442 reply_data = %{"status" => "1", "in_reply_to_status_id" => activity.id}
443
444 # public
445 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "public"))
446 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
447 assert object.data["repliesCount"] == 1
448
449 # unlisted
450 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "unlisted"))
451 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
452 assert object.data["repliesCount"] == 2
453
454 # private
455 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "private"))
456 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
457 assert object.data["repliesCount"] == 2
458
459 # direct
460 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct"))
461 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
462 assert object.data["repliesCount"] == 2
463 end
464 end
465
466 describe "fetch activities for recipients" do
467 test "retrieve the activities for certain recipients" do
468 {:ok, activity_one} = ActivityBuilder.insert(%{"to" => ["someone"]})
469 {:ok, activity_two} = ActivityBuilder.insert(%{"to" => ["someone_else"]})
470 {:ok, _activity_three} = ActivityBuilder.insert(%{"to" => ["noone"]})
471
472 activities = ActivityPub.fetch_activities(["someone", "someone_else"])
473 assert length(activities) == 2
474 assert activities == [activity_one, activity_two]
475 end
476 end
477
478 describe "fetch activities in context" do
479 test "retrieves activities that have a given context" do
480 {:ok, activity} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
481 {:ok, activity_two} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
482 {:ok, _activity_three} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"})
483 {:ok, _activity_four} = ActivityBuilder.insert(%{"type" => "Announce", "context" => "2hu"})
484 activity_five = insert(:note_activity)
485 user = insert(:user)
486
487 {:ok, user} = User.block(user, %{ap_id: activity_five.data["actor"]})
488
489 activities = ActivityPub.fetch_activities_for_context("2hu", %{"blocking_user" => user})
490 assert activities == [activity_two, activity]
491 end
492 end
493
494 test "doesn't return blocked activities" do
495 activity_one = insert(:note_activity)
496 activity_two = insert(:note_activity)
497 activity_three = insert(:note_activity)
498 user = insert(:user)
499 booster = insert(:user)
500 {:ok, user} = User.block(user, %{ap_id: activity_one.data["actor"]})
501
502 activities =
503 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
504
505 assert Enum.member?(activities, activity_two)
506 assert Enum.member?(activities, activity_three)
507 refute Enum.member?(activities, activity_one)
508
509 {:ok, user} = User.unblock(user, %{ap_id: activity_one.data["actor"]})
510
511 activities =
512 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
513
514 assert Enum.member?(activities, activity_two)
515 assert Enum.member?(activities, activity_three)
516 assert Enum.member?(activities, activity_one)
517
518 {:ok, user} = User.block(user, %{ap_id: activity_three.data["actor"]})
519 {:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
520 %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
521 activity_three = Activity.get_by_id(activity_three.id)
522
523 activities =
524 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
525
526 assert Enum.member?(activities, activity_two)
527 refute Enum.member?(activities, activity_three)
528 refute Enum.member?(activities, boost_activity)
529 assert Enum.member?(activities, activity_one)
530
531 activities =
532 ActivityPub.fetch_activities([], %{"blocking_user" => nil, "skip_preload" => true})
533
534 assert Enum.member?(activities, activity_two)
535 assert Enum.member?(activities, activity_three)
536 assert Enum.member?(activities, boost_activity)
537 assert Enum.member?(activities, activity_one)
538 end
539
540 test "doesn't return transitive interactions concerning blocked users" do
541 blocker = insert(:user)
542 blockee = insert(:user)
543 friend = insert(:user)
544
545 {:ok, blocker} = User.block(blocker, blockee)
546
547 {:ok, activity_one} = CommonAPI.post(friend, %{"status" => "hey!"})
548
549 {:ok, activity_two} = CommonAPI.post(friend, %{"status" => "hey! @#{blockee.nickname}"})
550
551 {:ok, activity_three} = CommonAPI.post(blockee, %{"status" => "hey! @#{friend.nickname}"})
552
553 {:ok, activity_four} = CommonAPI.post(blockee, %{"status" => "hey! @#{blocker.nickname}"})
554
555 activities = ActivityPub.fetch_activities([], %{"blocking_user" => blocker})
556
557 assert Enum.member?(activities, activity_one)
558 refute Enum.member?(activities, activity_two)
559 refute Enum.member?(activities, activity_three)
560 refute Enum.member?(activities, activity_four)
561 end
562
563 test "doesn't return announce activities concerning blocked users" do
564 blocker = insert(:user)
565 blockee = insert(:user)
566 friend = insert(:user)
567
568 {:ok, blocker} = User.block(blocker, blockee)
569
570 {:ok, activity_one} = CommonAPI.post(friend, %{"status" => "hey!"})
571
572 {:ok, activity_two} = CommonAPI.post(blockee, %{"status" => "hey! @#{friend.nickname}"})
573
574 {:ok, activity_three, _} = CommonAPI.repeat(activity_two.id, friend)
575
576 activities =
577 ActivityPub.fetch_activities([], %{"blocking_user" => blocker})
578 |> Enum.map(fn act -> act.id end)
579
580 assert Enum.member?(activities, activity_one.id)
581 refute Enum.member?(activities, activity_two.id)
582 refute Enum.member?(activities, activity_three.id)
583 end
584
585 test "doesn't return activities from blocked domains" do
586 domain = "dogwhistle.zone"
587 domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"})
588 note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})
589 activity = insert(:note_activity, %{note: note})
590 user = insert(:user)
591 {:ok, user} = User.block_domain(user, domain)
592
593 activities =
594 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
595
596 refute activity in activities
597
598 followed_user = insert(:user)
599 ActivityPub.follow(user, followed_user)
600 {:ok, repeat_activity, _} = CommonAPI.repeat(activity.id, followed_user)
601
602 activities =
603 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
604
605 refute repeat_activity in activities
606 end
607
608 test "does return activities from followed users on blocked domains" do
609 domain = "meanies.social"
610 domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"})
611 blocker = insert(:user)
612
613 {:ok, blocker} = User.follow(blocker, domain_user)
614 {:ok, blocker} = User.block_domain(blocker, domain)
615
616 assert User.following?(blocker, domain_user)
617 assert User.blocks_domain?(blocker, domain_user)
618 refute User.blocks?(blocker, domain_user)
619
620 note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})
621 activity = insert(:note_activity, %{note: note})
622
623 activities =
624 ActivityPub.fetch_activities([], %{"blocking_user" => blocker, "skip_preload" => true})
625
626 assert activity in activities
627
628 # And check that if the guy we DO follow boosts someone else from their domain, that should be hidden
629 another_user = insert(:user, %{ap_id: "https://#{domain}/@meanie2"})
630 bad_note = insert(:note, %{data: %{"actor" => another_user.ap_id}})
631 bad_activity = insert(:note_activity, %{note: bad_note})
632 {:ok, repeat_activity, _} = CommonAPI.repeat(bad_activity.id, domain_user)
633
634 activities =
635 ActivityPub.fetch_activities([], %{"blocking_user" => blocker, "skip_preload" => true})
636
637 refute repeat_activity in activities
638 end
639
640 test "doesn't return muted activities" do
641 activity_one = insert(:note_activity)
642 activity_two = insert(:note_activity)
643 activity_three = insert(:note_activity)
644 user = insert(:user)
645 booster = insert(:user)
646 {:ok, user} = User.mute(user, %User{ap_id: activity_one.data["actor"]})
647
648 activities =
649 ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
650
651 assert Enum.member?(activities, activity_two)
652 assert Enum.member?(activities, activity_three)
653 refute Enum.member?(activities, activity_one)
654
655 # Calling with 'with_muted' will deliver muted activities, too.
656 activities =
657 ActivityPub.fetch_activities([], %{
658 "muting_user" => user,
659 "with_muted" => true,
660 "skip_preload" => true
661 })
662
663 assert Enum.member?(activities, activity_two)
664 assert Enum.member?(activities, activity_three)
665 assert Enum.member?(activities, activity_one)
666
667 {:ok, user} = User.unmute(user, %User{ap_id: activity_one.data["actor"]})
668
669 activities =
670 ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
671
672 assert Enum.member?(activities, activity_two)
673 assert Enum.member?(activities, activity_three)
674 assert Enum.member?(activities, activity_one)
675
676 {:ok, user} = User.mute(user, %User{ap_id: activity_three.data["actor"]})
677 {:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
678 %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
679 activity_three = Activity.get_by_id(activity_three.id)
680
681 activities =
682 ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
683
684 assert Enum.member?(activities, activity_two)
685 refute Enum.member?(activities, activity_three)
686 refute Enum.member?(activities, boost_activity)
687 assert Enum.member?(activities, activity_one)
688
689 activities = ActivityPub.fetch_activities([], %{"muting_user" => nil, "skip_preload" => true})
690
691 assert Enum.member?(activities, activity_two)
692 assert Enum.member?(activities, activity_three)
693 assert Enum.member?(activities, boost_activity)
694 assert Enum.member?(activities, activity_one)
695 end
696
697 test "doesn't return thread muted activities" do
698 user = insert(:user)
699 _activity_one = insert(:note_activity)
700 note_two = insert(:note, data: %{"context" => "suya.."})
701 activity_two = insert(:note_activity, note: note_two)
702
703 {:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
704
705 assert [_activity_one] = ActivityPub.fetch_activities([], %{"muting_user" => user})
706 end
707
708 test "returns thread muted activities when with_muted is set" do
709 user = insert(:user)
710 _activity_one = insert(:note_activity)
711 note_two = insert(:note, data: %{"context" => "suya.."})
712 activity_two = insert(:note_activity, note: note_two)
713
714 {:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
715
716 assert [_activity_two, _activity_one] =
717 ActivityPub.fetch_activities([], %{"muting_user" => user, "with_muted" => true})
718 end
719
720 test "does include announces on request" do
721 activity_three = insert(:note_activity)
722 user = insert(:user)
723 booster = insert(:user)
724
725 {:ok, user} = User.follow(user, booster)
726
727 {:ok, announce, _object} = CommonAPI.repeat(activity_three.id, booster)
728
729 [announce_activity] = ActivityPub.fetch_activities([user.ap_id | User.following(user)])
730
731 assert announce_activity.id == announce.id
732 end
733
734 test "excludes reblogs on request" do
735 user = insert(:user)
736 {:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Create"}, %{:user => user})
737 {:ok, _} = ActivityBuilder.insert(%{"type" => "Announce"}, %{:user => user})
738
739 [activity] = ActivityPub.fetch_user_activities(user, nil, %{"exclude_reblogs" => "true"})
740
741 assert activity == expected_activity
742 end
743
744 describe "public fetch activities" do
745 test "doesn't retrieve unlisted activities" do
746 user = insert(:user)
747
748 {:ok, _unlisted_activity} =
749 CommonAPI.post(user, %{"status" => "yeah", "visibility" => "unlisted"})
750
751 {:ok, listed_activity} = CommonAPI.post(user, %{"status" => "yeah"})
752
753 [activity] = ActivityPub.fetch_public_activities()
754
755 assert activity == listed_activity
756 end
757
758 test "retrieves public activities" do
759 _activities = ActivityPub.fetch_public_activities()
760
761 %{public: public} = ActivityBuilder.public_and_non_public()
762
763 activities = ActivityPub.fetch_public_activities()
764 assert length(activities) == 1
765 assert Enum.at(activities, 0) == public
766 end
767
768 test "retrieves a maximum of 20 activities" do
769 ActivityBuilder.insert_list(10)
770 expected_activities = ActivityBuilder.insert_list(20)
771
772 activities = ActivityPub.fetch_public_activities()
773
774 assert collect_ids(activities) == collect_ids(expected_activities)
775 assert length(activities) == 20
776 end
777
778 test "retrieves ids starting from a since_id" do
779 activities = ActivityBuilder.insert_list(30)
780 expected_activities = ActivityBuilder.insert_list(10)
781 since_id = List.last(activities).id
782
783 activities = ActivityPub.fetch_public_activities(%{"since_id" => since_id})
784
785 assert collect_ids(activities) == collect_ids(expected_activities)
786 assert length(activities) == 10
787 end
788
789 test "retrieves ids up to max_id" do
790 ActivityBuilder.insert_list(10)
791 expected_activities = ActivityBuilder.insert_list(20)
792
793 %{id: max_id} =
794 10
795 |> ActivityBuilder.insert_list()
796 |> List.first()
797
798 activities = ActivityPub.fetch_public_activities(%{"max_id" => max_id})
799
800 assert length(activities) == 20
801 assert collect_ids(activities) == collect_ids(expected_activities)
802 end
803
804 test "paginates via offset/limit" do
805 _first_part_activities = ActivityBuilder.insert_list(10)
806 second_part_activities = ActivityBuilder.insert_list(10)
807
808 later_activities = ActivityBuilder.insert_list(10)
809
810 activities =
811 ActivityPub.fetch_public_activities(%{"page" => "2", "page_size" => "20"}, :offset)
812
813 assert length(activities) == 20
814
815 assert collect_ids(activities) ==
816 collect_ids(second_part_activities) ++ collect_ids(later_activities)
817 end
818
819 test "doesn't return reblogs for users for whom reblogs have been muted" do
820 activity = insert(:note_activity)
821 user = insert(:user)
822 booster = insert(:user)
823 {:ok, user} = CommonAPI.hide_reblogs(user, booster)
824
825 {:ok, activity, _} = CommonAPI.repeat(activity.id, booster)
826
827 activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
828
829 refute Enum.any?(activities, fn %{id: id} -> id == activity.id end)
830 end
831
832 test "returns reblogs for users for whom reblogs have not been muted" do
833 activity = insert(:note_activity)
834 user = insert(:user)
835 booster = insert(:user)
836 {:ok, user} = CommonAPI.hide_reblogs(user, booster)
837 {:ok, user} = CommonAPI.show_reblogs(user, booster)
838
839 {:ok, activity, _} = CommonAPI.repeat(activity.id, booster)
840
841 activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
842
843 assert Enum.any?(activities, fn %{id: id} -> id == activity.id end)
844 end
845 end
846
847 describe "react to an object" do
848 test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
849 Pleroma.Config.put([:instance, :federating], true)
850 user = insert(:user)
851 reactor = insert(:user)
852 {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
853 assert object = Object.normalize(activity)
854
855 {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥")
856
857 assert called(Pleroma.Web.Federator.publish(reaction_activity))
858 end
859
860 test "adds an emoji reaction activity to the db" do
861 user = insert(:user)
862 reactor = insert(:user)
863 {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
864 assert object = Object.normalize(activity)
865
866 {:ok, reaction_activity, object} = ActivityPub.react_with_emoji(reactor, object, "🔥")
867
868 assert reaction_activity
869
870 assert reaction_activity.data["actor"] == reactor.ap_id
871 assert reaction_activity.data["type"] == "EmojiReaction"
872 assert reaction_activity.data["content"] == "🔥"
873 assert reaction_activity.data["object"] == object.data["id"]
874 assert reaction_activity.data["to"] == [User.ap_followers(reactor), activity.data["actor"]]
875 assert reaction_activity.data["context"] == object.data["context"]
876 assert object.data["reaction_count"] == 1
877 assert object.data["reactions"]["🔥"] == [reactor.ap_id]
878 end
879 end
880
881 describe "unreacting to an object" do
882 test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
883 Pleroma.Config.put([:instance, :federating], true)
884 user = insert(:user)
885 reactor = insert(:user)
886 {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
887 assert object = Object.normalize(activity)
888
889 {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥")
890
891 assert called(Pleroma.Web.Federator.publish(reaction_activity))
892
893 {:ok, unreaction_activity, _object} =
894 ActivityPub.unreact_with_emoji(reactor, reaction_activity.data["id"])
895
896 assert called(Pleroma.Web.Federator.publish(unreaction_activity))
897 end
898
899 test "adds an undo activity to the db" do
900 user = insert(:user)
901 reactor = insert(:user)
902 {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
903 assert object = Object.normalize(activity)
904
905 {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥")
906
907 {:ok, unreaction_activity, _object} =
908 ActivityPub.unreact_with_emoji(reactor, reaction_activity.data["id"])
909
910 assert unreaction_activity.actor == reactor.ap_id
911 assert unreaction_activity.data["object"] == reaction_activity.data["id"]
912
913 object = Object.get_by_ap_id(object.data["id"])
914 assert object.data["reaction_count"] == 0
915 assert object.data["reactions"] == %{}
916 end
917 end
918
919 describe "like an object" do
920 test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
921 Pleroma.Config.put([:instance, :federating], true)
922 note_activity = insert(:note_activity)
923 assert object_activity = Object.normalize(note_activity)
924
925 user = insert(:user)
926
927 {:ok, like_activity, _object} = ActivityPub.like(user, object_activity)
928 assert called(Pleroma.Web.Federator.publish(like_activity))
929 end
930
931 test "returns exist activity if object already liked" do
932 note_activity = insert(:note_activity)
933 assert object_activity = Object.normalize(note_activity)
934
935 user = insert(:user)
936
937 {:ok, like_activity, _object} = ActivityPub.like(user, object_activity)
938
939 {:ok, like_activity_exist, _object} = ActivityPub.like(user, object_activity)
940 assert like_activity == like_activity_exist
941 end
942
943 test "adds a like activity to the db" do
944 note_activity = insert(:note_activity)
945 assert object = Object.normalize(note_activity)
946
947 user = insert(:user)
948 user_two = insert(:user)
949
950 {:ok, like_activity, object} = ActivityPub.like(user, object)
951
952 assert like_activity.data["actor"] == user.ap_id
953 assert like_activity.data["type"] == "Like"
954 assert like_activity.data["object"] == object.data["id"]
955 assert like_activity.data["to"] == [User.ap_followers(user), note_activity.data["actor"]]
956 assert like_activity.data["context"] == object.data["context"]
957 assert object.data["like_count"] == 1
958 assert object.data["likes"] == [user.ap_id]
959
960 # Just return the original activity if the user already liked it.
961 {:ok, same_like_activity, object} = ActivityPub.like(user, object)
962
963 assert like_activity == same_like_activity
964 assert object.data["likes"] == [user.ap_id]
965 assert object.data["like_count"] == 1
966
967 {:ok, _like_activity, object} = ActivityPub.like(user_two, object)
968 assert object.data["like_count"] == 2
969 end
970 end
971
972 describe "unliking" do
973 test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
974 Pleroma.Config.put([:instance, :federating], true)
975
976 note_activity = insert(:note_activity)
977 object = Object.normalize(note_activity)
978 user = insert(:user)
979
980 {:ok, object} = ActivityPub.unlike(user, object)
981 refute called(Pleroma.Web.Federator.publish())
982
983 {:ok, _like_activity, object} = ActivityPub.like(user, object)
984 assert object.data["like_count"] == 1
985
986 {:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object)
987 assert object.data["like_count"] == 0
988
989 assert called(Pleroma.Web.Federator.publish(unlike_activity))
990 end
991
992 test "unliking a previously liked object" do
993 note_activity = insert(:note_activity)
994 object = Object.normalize(note_activity)
995 user = insert(:user)
996
997 # Unliking something that hasn't been liked does nothing
998 {:ok, object} = ActivityPub.unlike(user, object)
999 assert object.data["like_count"] == 0
1000
1001 {:ok, like_activity, object} = ActivityPub.like(user, object)
1002 assert object.data["like_count"] == 1
1003
1004 {:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object)
1005 assert object.data["like_count"] == 0
1006
1007 assert Activity.get_by_id(like_activity.id) == nil
1008 assert note_activity.actor in unlike_activity.recipients
1009 end
1010 end
1011
1012 describe "announcing an object" do
1013 test "adds an announce activity to the db" do
1014 note_activity = insert(:note_activity)
1015 object = Object.normalize(note_activity)
1016 user = insert(:user)
1017
1018 {:ok, announce_activity, object} = ActivityPub.announce(user, object)
1019 assert object.data["announcement_count"] == 1
1020 assert object.data["announcements"] == [user.ap_id]
1021
1022 assert announce_activity.data["to"] == [
1023 User.ap_followers(user),
1024 note_activity.data["actor"]
1025 ]
1026
1027 assert announce_activity.data["object"] == object.data["id"]
1028 assert announce_activity.data["actor"] == user.ap_id
1029 assert announce_activity.data["context"] == object.data["context"]
1030 end
1031 end
1032
1033 describe "announcing a private object" do
1034 test "adds an announce activity to the db if the audience is not widened" do
1035 user = insert(:user)
1036 {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
1037 object = Object.normalize(note_activity)
1038
1039 {:ok, announce_activity, object} = ActivityPub.announce(user, object, nil, true, false)
1040
1041 assert announce_activity.data["to"] == [User.ap_followers(user)]
1042
1043 assert announce_activity.data["object"] == object.data["id"]
1044 assert announce_activity.data["actor"] == user.ap_id
1045 assert announce_activity.data["context"] == object.data["context"]
1046 end
1047
1048 test "does not add an announce activity to the db if the audience is widened" do
1049 user = insert(:user)
1050 {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
1051 object = Object.normalize(note_activity)
1052
1053 assert {:error, _} = ActivityPub.announce(user, object, nil, true, true)
1054 end
1055
1056 test "does not add an announce activity to the db if the announcer is not the author" do
1057 user = insert(:user)
1058 announcer = insert(:user)
1059 {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
1060 object = Object.normalize(note_activity)
1061
1062 assert {:error, _} = ActivityPub.announce(announcer, object, nil, true, false)
1063 end
1064 end
1065
1066 describe "unannouncing an object" do
1067 test "unannouncing a previously announced object" do
1068 note_activity = insert(:note_activity)
1069 object = Object.normalize(note_activity)
1070 user = insert(:user)
1071
1072 # Unannouncing an object that is not announced does nothing
1073 # {:ok, object} = ActivityPub.unannounce(user, object)
1074 # assert object.data["announcement_count"] == 0
1075
1076 {:ok, announce_activity, object} = ActivityPub.announce(user, object)
1077 assert object.data["announcement_count"] == 1
1078
1079 {:ok, unannounce_activity, object} = ActivityPub.unannounce(user, object)
1080 assert object.data["announcement_count"] == 0
1081
1082 assert unannounce_activity.data["to"] == [
1083 User.ap_followers(user),
1084 object.data["actor"]
1085 ]
1086
1087 assert unannounce_activity.data["type"] == "Undo"
1088 assert unannounce_activity.data["object"] == announce_activity.data
1089 assert unannounce_activity.data["actor"] == user.ap_id
1090 assert unannounce_activity.data["context"] == announce_activity.data["context"]
1091
1092 assert Activity.get_by_id(announce_activity.id) == nil
1093 end
1094 end
1095
1096 describe "uploading files" do
1097 test "copies the file to the configured folder" do
1098 file = %Plug.Upload{
1099 content_type: "image/jpg",
1100 path: Path.absname("test/fixtures/image.jpg"),
1101 filename: "an_image.jpg"
1102 }
1103
1104 {:ok, %Object{} = object} = ActivityPub.upload(file)
1105 assert object.data["name"] == "an_image.jpg"
1106 end
1107
1108 test "works with base64 encoded images" do
1109 file = %{
1110 "img" => data_uri()
1111 }
1112
1113 {:ok, %Object{}} = ActivityPub.upload(file)
1114 end
1115 end
1116
1117 describe "fetch the latest Follow" do
1118 test "fetches the latest Follow activity" do
1119 %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
1120 follower = Repo.get_by(User, ap_id: activity.data["actor"])
1121 followed = Repo.get_by(User, ap_id: activity.data["object"])
1122
1123 assert activity == Utils.fetch_latest_follow(follower, followed)
1124 end
1125 end
1126
1127 describe "following / unfollowing" do
1128 test "creates a follow activity" do
1129 follower = insert(:user)
1130 followed = insert(:user)
1131
1132 {:ok, activity} = ActivityPub.follow(follower, followed)
1133 assert activity.data["type"] == "Follow"
1134 assert activity.data["actor"] == follower.ap_id
1135 assert activity.data["object"] == followed.ap_id
1136 end
1137
1138 test "creates an undo activity for the last follow" do
1139 follower = insert(:user)
1140 followed = insert(:user)
1141
1142 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
1143 {:ok, activity} = ActivityPub.unfollow(follower, followed)
1144
1145 assert activity.data["type"] == "Undo"
1146 assert activity.data["actor"] == follower.ap_id
1147
1148 embedded_object = activity.data["object"]
1149 assert is_map(embedded_object)
1150 assert embedded_object["type"] == "Follow"
1151 assert embedded_object["object"] == followed.ap_id
1152 assert embedded_object["id"] == follow_activity.data["id"]
1153 end
1154 end
1155
1156 describe "blocking / unblocking" do
1157 test "creates a block activity" do
1158 blocker = insert(:user)
1159 blocked = insert(:user)
1160
1161 {:ok, activity} = ActivityPub.block(blocker, blocked)
1162
1163 assert activity.data["type"] == "Block"
1164 assert activity.data["actor"] == blocker.ap_id
1165 assert activity.data["object"] == blocked.ap_id
1166 end
1167
1168 test "creates an undo activity for the last block" do
1169 blocker = insert(:user)
1170 blocked = insert(:user)
1171
1172 {:ok, block_activity} = ActivityPub.block(blocker, blocked)
1173 {:ok, activity} = ActivityPub.unblock(blocker, blocked)
1174
1175 assert activity.data["type"] == "Undo"
1176 assert activity.data["actor"] == blocker.ap_id
1177
1178 embedded_object = activity.data["object"]
1179 assert is_map(embedded_object)
1180 assert embedded_object["type"] == "Block"
1181 assert embedded_object["object"] == blocked.ap_id
1182 assert embedded_object["id"] == block_activity.data["id"]
1183 end
1184 end
1185
1186 describe "deletion" do
1187 test "it creates a delete activity and deletes the original object" do
1188 note = insert(:note_activity)
1189 object = Object.normalize(note)
1190 {:ok, delete} = ActivityPub.delete(object)
1191
1192 assert delete.data["type"] == "Delete"
1193 assert delete.data["actor"] == note.data["actor"]
1194 assert delete.data["object"] == object.data["id"]
1195
1196 assert Activity.get_by_id(delete.id) != nil
1197
1198 assert Repo.get(Object, object.id).data["type"] == "Tombstone"
1199 end
1200
1201 test "decrements user note count only for public activities" do
1202 user = insert(:user, note_count: 10)
1203
1204 {:ok, a1} =
1205 CommonAPI.post(User.get_cached_by_id(user.id), %{
1206 "status" => "yeah",
1207 "visibility" => "public"
1208 })
1209
1210 {:ok, a2} =
1211 CommonAPI.post(User.get_cached_by_id(user.id), %{
1212 "status" => "yeah",
1213 "visibility" => "unlisted"
1214 })
1215
1216 {:ok, a3} =
1217 CommonAPI.post(User.get_cached_by_id(user.id), %{
1218 "status" => "yeah",
1219 "visibility" => "private"
1220 })
1221
1222 {:ok, a4} =
1223 CommonAPI.post(User.get_cached_by_id(user.id), %{
1224 "status" => "yeah",
1225 "visibility" => "direct"
1226 })
1227
1228 {:ok, _} = Object.normalize(a1) |> ActivityPub.delete()
1229 {:ok, _} = Object.normalize(a2) |> ActivityPub.delete()
1230 {:ok, _} = Object.normalize(a3) |> ActivityPub.delete()
1231 {:ok, _} = Object.normalize(a4) |> ActivityPub.delete()
1232
1233 user = User.get_cached_by_id(user.id)
1234 assert user.note_count == 10
1235 end
1236
1237 test "it creates a delete activity and checks that it is also sent to users mentioned by the deleted object" do
1238 user = insert(:user)
1239 note = insert(:note_activity)
1240 object = Object.normalize(note)
1241
1242 {:ok, object} =
1243 object
1244 |> Object.change(%{
1245 data: %{
1246 "actor" => object.data["actor"],
1247 "id" => object.data["id"],
1248 "to" => [user.ap_id],
1249 "type" => "Note"
1250 }
1251 })
1252 |> Object.update_and_set_cache()
1253
1254 {:ok, delete} = ActivityPub.delete(object)
1255
1256 assert user.ap_id in delete.data["to"]
1257 end
1258
1259 test "decreases reply count" do
1260 user = insert(:user)
1261 user2 = insert(:user)
1262
1263 {:ok, activity} = CommonAPI.post(user, %{"status" => "1", "visibility" => "public"})
1264 reply_data = %{"status" => "1", "in_reply_to_status_id" => activity.id}
1265 ap_id = activity.data["id"]
1266
1267 {:ok, public_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "public"))
1268 {:ok, unlisted_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "unlisted"))
1269 {:ok, private_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "private"))
1270 {:ok, direct_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct"))
1271
1272 _ = CommonAPI.delete(direct_reply.id, user2)
1273 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1274 assert object.data["repliesCount"] == 2
1275
1276 _ = CommonAPI.delete(private_reply.id, user2)
1277 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1278 assert object.data["repliesCount"] == 2
1279
1280 _ = CommonAPI.delete(public_reply.id, user2)
1281 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1282 assert object.data["repliesCount"] == 1
1283
1284 _ = CommonAPI.delete(unlisted_reply.id, user2)
1285 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1286 assert object.data["repliesCount"] == 0
1287 end
1288 end
1289
1290 describe "timeline post-processing" do
1291 test "it filters broken threads" do
1292 user1 = insert(:user)
1293 user2 = insert(:user)
1294 user3 = insert(:user)
1295
1296 {:ok, user1} = User.follow(user1, user3)
1297 assert User.following?(user1, user3)
1298
1299 {:ok, user2} = User.follow(user2, user3)
1300 assert User.following?(user2, user3)
1301
1302 {:ok, user3} = User.follow(user3, user2)
1303 assert User.following?(user3, user2)
1304
1305 {:ok, public_activity} = CommonAPI.post(user3, %{"status" => "hi 1"})
1306
1307 {:ok, private_activity_1} =
1308 CommonAPI.post(user3, %{"status" => "hi 2", "visibility" => "private"})
1309
1310 {:ok, private_activity_2} =
1311 CommonAPI.post(user2, %{
1312 "status" => "hi 3",
1313 "visibility" => "private",
1314 "in_reply_to_status_id" => private_activity_1.id
1315 })
1316
1317 {:ok, private_activity_3} =
1318 CommonAPI.post(user3, %{
1319 "status" => "hi 4",
1320 "visibility" => "private",
1321 "in_reply_to_status_id" => private_activity_2.id
1322 })
1323
1324 activities =
1325 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)])
1326 |> Enum.map(fn a -> a.id end)
1327
1328 private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"])
1329
1330 assert [public_activity.id, private_activity_1.id, private_activity_3.id] == activities
1331
1332 assert length(activities) == 3
1333
1334 activities =
1335 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)], %{"user" => user1})
1336 |> Enum.map(fn a -> a.id end)
1337
1338 assert [public_activity.id, private_activity_1.id] == activities
1339 assert length(activities) == 2
1340 end
1341 end
1342
1343 describe "update" do
1344 test "it creates an update activity with the new user data" do
1345 user = insert(:user)
1346 {:ok, user} = User.ensure_keys_present(user)
1347 user_data = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
1348
1349 {:ok, update} =
1350 ActivityPub.update(%{
1351 actor: user_data["id"],
1352 to: [user.follower_address],
1353 cc: [],
1354 object: user_data
1355 })
1356
1357 assert update.data["actor"] == user.ap_id
1358 assert update.data["to"] == [user.follower_address]
1359 assert embedded_object = update.data["object"]
1360 assert embedded_object["id"] == user_data["id"]
1361 assert embedded_object["type"] == user_data["type"]
1362 end
1363 end
1364
1365 test "returned pinned statuses" do
1366 Pleroma.Config.put([:instance, :max_pinned_statuses], 3)
1367 user = insert(:user)
1368
1369 {:ok, activity_one} = CommonAPI.post(user, %{"status" => "HI!!!"})
1370 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
1371 {:ok, activity_three} = CommonAPI.post(user, %{"status" => "HI!!!"})
1372
1373 CommonAPI.pin(activity_one.id, user)
1374 user = refresh_record(user)
1375
1376 CommonAPI.pin(activity_two.id, user)
1377 user = refresh_record(user)
1378
1379 CommonAPI.pin(activity_three.id, user)
1380 user = refresh_record(user)
1381
1382 activities = ActivityPub.fetch_user_activities(user, nil, %{"pinned" => "true"})
1383
1384 assert 3 = length(activities)
1385 end
1386
1387 describe "flag/1" do
1388 setup do
1389 reporter = insert(:user)
1390 target_account = insert(:user)
1391 content = "foobar"
1392 {:ok, activity} = CommonAPI.post(target_account, %{"status" => content})
1393 context = Utils.generate_context_id()
1394
1395 reporter_ap_id = reporter.ap_id
1396 target_ap_id = target_account.ap_id
1397 activity_ap_id = activity.data["id"]
1398
1399 activity_with_object = Activity.get_by_ap_id_with_object(activity_ap_id)
1400
1401 {:ok,
1402 %{
1403 reporter: reporter,
1404 context: context,
1405 target_account: target_account,
1406 reported_activity: activity,
1407 content: content,
1408 activity_ap_id: activity_ap_id,
1409 activity_with_object: activity_with_object,
1410 reporter_ap_id: reporter_ap_id,
1411 target_ap_id: target_ap_id
1412 }}
1413 end
1414
1415 test "it can create a Flag activity",
1416 %{
1417 reporter: reporter,
1418 context: context,
1419 target_account: target_account,
1420 reported_activity: reported_activity,
1421 content: content,
1422 activity_ap_id: activity_ap_id,
1423 activity_with_object: activity_with_object,
1424 reporter_ap_id: reporter_ap_id,
1425 target_ap_id: target_ap_id
1426 } do
1427 assert {:ok, activity} =
1428 ActivityPub.flag(%{
1429 actor: reporter,
1430 context: context,
1431 account: target_account,
1432 statuses: [reported_activity],
1433 content: content
1434 })
1435
1436 note_obj = %{
1437 "type" => "Note",
1438 "id" => activity_ap_id,
1439 "content" => content,
1440 "published" => activity_with_object.object.data["published"],
1441 "actor" => AccountView.render("show.json", %{user: target_account})
1442 }
1443
1444 assert %Activity{
1445 actor: ^reporter_ap_id,
1446 data: %{
1447 "type" => "Flag",
1448 "content" => ^content,
1449 "context" => ^context,
1450 "object" => [^target_ap_id, ^note_obj]
1451 }
1452 } = activity
1453 end
1454
1455 test_with_mock "strips status data from Flag, before federating it",
1456 %{
1457 reporter: reporter,
1458 context: context,
1459 target_account: target_account,
1460 reported_activity: reported_activity,
1461 content: content
1462 },
1463 Utils,
1464 [:passthrough],
1465 [] do
1466 {:ok, activity} =
1467 ActivityPub.flag(%{
1468 actor: reporter,
1469 context: context,
1470 account: target_account,
1471 statuses: [reported_activity],
1472 content: content
1473 })
1474
1475 new_data =
1476 put_in(activity.data, ["object"], [target_account.ap_id, reported_activity.data["id"]])
1477
1478 assert_called(Utils.maybe_federate(%{activity | data: new_data}))
1479 end
1480 end
1481
1482 test "fetch_activities/2 returns activities addressed to a list " do
1483 user = insert(:user)
1484 member = insert(:user)
1485 {:ok, list} = Pleroma.List.create("foo", user)
1486 {:ok, list} = Pleroma.List.follow(list, member)
1487
1488 {:ok, activity} =
1489 CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"})
1490
1491 activity = Repo.preload(activity, :bookmark)
1492 activity = %Activity{activity | thread_muted?: !!activity.thread_muted?}
1493
1494 assert ActivityPub.fetch_activities([], %{"user" => user}) == [activity]
1495 end
1496
1497 def data_uri do
1498 File.read!("test/fixtures/avatar_data_uri")
1499 end
1500
1501 describe "fetch_activities_bounded" do
1502 test "fetches private posts for followed users" do
1503 user = insert(:user)
1504
1505 {:ok, activity} =
1506 CommonAPI.post(user, %{
1507 "status" => "thought I looked cute might delete later :3",
1508 "visibility" => "private"
1509 })
1510
1511 [result] = ActivityPub.fetch_activities_bounded([user.follower_address], [])
1512 assert result.id == activity.id
1513 end
1514
1515 test "fetches only public posts for other users" do
1516 user = insert(:user)
1517 {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe", "visibility" => "public"})
1518
1519 {:ok, _private_activity} =
1520 CommonAPI.post(user, %{
1521 "status" => "why is tenshi eating a corndog so cute?",
1522 "visibility" => "private"
1523 })
1524
1525 [result] = ActivityPub.fetch_activities_bounded([], [user.follower_address])
1526 assert result.id == activity.id
1527 end
1528 end
1529
1530 describe "fetch_follow_information_for_user" do
1531 test "syncronizes following/followers counters" do
1532 user =
1533 insert(:user,
1534 local: false,
1535 follower_address: "http://localhost:4001/users/fuser2/followers",
1536 following_address: "http://localhost:4001/users/fuser2/following"
1537 )
1538
1539 {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
1540 assert info.follower_count == 527
1541 assert info.following_count == 267
1542 end
1543
1544 test "detects hidden followers" do
1545 mock(fn env ->
1546 case env.url do
1547 "http://localhost:4001/users/masto_closed/followers?page=1" ->
1548 %Tesla.Env{status: 403, body: ""}
1549
1550 _ ->
1551 apply(HttpRequestMock, :request, [env])
1552 end
1553 end)
1554
1555 user =
1556 insert(:user,
1557 local: false,
1558 follower_address: "http://localhost:4001/users/masto_closed/followers",
1559 following_address: "http://localhost:4001/users/masto_closed/following"
1560 )
1561
1562 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1563 assert follow_info.hide_followers == true
1564 assert follow_info.hide_follows == false
1565 end
1566
1567 test "detects hidden follows" do
1568 mock(fn env ->
1569 case env.url do
1570 "http://localhost:4001/users/masto_closed/following?page=1" ->
1571 %Tesla.Env{status: 403, body: ""}
1572
1573 _ ->
1574 apply(HttpRequestMock, :request, [env])
1575 end
1576 end)
1577
1578 user =
1579 insert(:user,
1580 local: false,
1581 follower_address: "http://localhost:4001/users/masto_closed/followers",
1582 following_address: "http://localhost:4001/users/masto_closed/following"
1583 )
1584
1585 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1586 assert follow_info.hide_followers == false
1587 assert follow_info.hide_follows == true
1588 end
1589 end
1590 end