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