Preload thread mutes/bookmarks in user_statuses
[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 describe "streaming out participations" do
25 test "it streams them out" do
26 user = insert(:user)
27 {:ok, activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
28
29 {:ok, conversation} = Pleroma.Conversation.create_or_bump_for(activity)
30
31 participations =
32 conversation.participations
33 |> Repo.preload(:user)
34
35 with_mock Pleroma.Web.Streamer,
36 stream: fn _, _ -> nil end do
37 ActivityPub.stream_out_participations(conversation.participations)
38
39 Enum.each(participations, fn participation ->
40 assert called(Pleroma.Web.Streamer.stream("participation", participation))
41 end)
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 "create activities" do
261 test "removes doubled 'to' recipients" do
262 user = insert(:user)
263
264 {:ok, activity} =
265 ActivityPub.create(%{
266 to: ["user1", "user1", "user2"],
267 actor: user,
268 context: "",
269 object: %{
270 "to" => ["user1", "user1", "user2"],
271 "type" => "Note",
272 "content" => "testing"
273 }
274 })
275
276 assert activity.data["to"] == ["user1", "user2"]
277 assert activity.actor == user.ap_id
278 assert activity.recipients == ["user1", "user2", user.ap_id]
279 end
280
281 test "increases user note count only for public activities" do
282 user = insert(:user)
283
284 {:ok, _} =
285 CommonAPI.post(User.get_cached_by_id(user.id), %{
286 "status" => "1",
287 "visibility" => "public"
288 })
289
290 {:ok, _} =
291 CommonAPI.post(User.get_cached_by_id(user.id), %{
292 "status" => "2",
293 "visibility" => "unlisted"
294 })
295
296 {:ok, _} =
297 CommonAPI.post(User.get_cached_by_id(user.id), %{
298 "status" => "2",
299 "visibility" => "private"
300 })
301
302 {:ok, _} =
303 CommonAPI.post(User.get_cached_by_id(user.id), %{
304 "status" => "3",
305 "visibility" => "direct"
306 })
307
308 user = User.get_cached_by_id(user.id)
309 assert user.info.note_count == 2
310 end
311
312 test "increases replies count" do
313 user = insert(:user)
314 user2 = insert(:user)
315
316 {:ok, activity} = CommonAPI.post(user, %{"status" => "1", "visibility" => "public"})
317 ap_id = activity.data["id"]
318 reply_data = %{"status" => "1", "in_reply_to_status_id" => activity.id}
319
320 # public
321 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "public"))
322 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
323 assert object.data["repliesCount"] == 1
324
325 # unlisted
326 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "unlisted"))
327 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
328 assert object.data["repliesCount"] == 2
329
330 # private
331 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "private"))
332 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
333 assert object.data["repliesCount"] == 2
334
335 # direct
336 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct"))
337 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
338 assert object.data["repliesCount"] == 2
339 end
340 end
341
342 describe "fetch activities for recipients" do
343 test "retrieve the activities for certain recipients" do
344 {:ok, activity_one} = ActivityBuilder.insert(%{"to" => ["someone"]})
345 {:ok, activity_two} = ActivityBuilder.insert(%{"to" => ["someone_else"]})
346 {:ok, _activity_three} = ActivityBuilder.insert(%{"to" => ["noone"]})
347
348 activities = ActivityPub.fetch_activities(["someone", "someone_else"])
349 assert length(activities) == 2
350 assert activities == [activity_one, activity_two]
351 end
352 end
353
354 describe "fetch activities in context" do
355 test "retrieves activities that have a given context" do
356 {:ok, activity} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
357 {:ok, activity_two} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
358 {:ok, _activity_three} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"})
359 {:ok, _activity_four} = ActivityBuilder.insert(%{"type" => "Announce", "context" => "2hu"})
360 activity_five = insert(:note_activity)
361 user = insert(:user)
362
363 {:ok, user} = User.block(user, %{ap_id: activity_five.data["actor"]})
364
365 activities = ActivityPub.fetch_activities_for_context("2hu", %{"blocking_user" => user})
366 assert activities == [activity_two, activity]
367 end
368 end
369
370 test "doesn't return blocked activities" do
371 activity_one = insert(:note_activity)
372 activity_two = insert(:note_activity)
373 activity_three = insert(:note_activity)
374 user = insert(:user)
375 booster = insert(:user)
376 {:ok, user} = User.block(user, %{ap_id: activity_one.data["actor"]})
377
378 activities =
379 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
380
381 assert Enum.member?(activities, activity_two)
382 assert Enum.member?(activities, activity_three)
383 refute Enum.member?(activities, activity_one)
384
385 {:ok, user} = User.unblock(user, %{ap_id: activity_one.data["actor"]})
386
387 activities =
388 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
389
390 assert Enum.member?(activities, activity_two)
391 assert Enum.member?(activities, activity_three)
392 assert Enum.member?(activities, activity_one)
393
394 {:ok, user} = User.block(user, %{ap_id: activity_three.data["actor"]})
395 {:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
396 %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
397 activity_three = Activity.get_by_id(activity_three.id)
398
399 activities =
400 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
401
402 assert Enum.member?(activities, activity_two)
403 refute Enum.member?(activities, activity_three)
404 refute Enum.member?(activities, boost_activity)
405 assert Enum.member?(activities, activity_one)
406
407 activities =
408 ActivityPub.fetch_activities([], %{"blocking_user" => nil, "skip_preload" => true})
409
410 assert Enum.member?(activities, activity_two)
411 assert Enum.member?(activities, activity_three)
412 assert Enum.member?(activities, boost_activity)
413 assert Enum.member?(activities, activity_one)
414 end
415
416 test "doesn't return transitive interactions concerning blocked users" do
417 blocker = insert(:user)
418 blockee = insert(:user)
419 friend = insert(:user)
420
421 {:ok, blocker} = User.block(blocker, blockee)
422
423 {:ok, activity_one} = CommonAPI.post(friend, %{"status" => "hey!"})
424
425 {:ok, activity_two} = CommonAPI.post(friend, %{"status" => "hey! @#{blockee.nickname}"})
426
427 {:ok, activity_three} = CommonAPI.post(blockee, %{"status" => "hey! @#{friend.nickname}"})
428
429 {:ok, activity_four} = CommonAPI.post(blockee, %{"status" => "hey! @#{blocker.nickname}"})
430
431 activities = ActivityPub.fetch_activities([], %{"blocking_user" => blocker})
432
433 assert Enum.member?(activities, activity_one)
434 refute Enum.member?(activities, activity_two)
435 refute Enum.member?(activities, activity_three)
436 refute Enum.member?(activities, activity_four)
437 end
438
439 test "doesn't return announce activities concerning blocked users" do
440 blocker = insert(:user)
441 blockee = insert(:user)
442 friend = insert(:user)
443
444 {:ok, blocker} = User.block(blocker, blockee)
445
446 {:ok, activity_one} = CommonAPI.post(friend, %{"status" => "hey!"})
447
448 {:ok, activity_two} = CommonAPI.post(blockee, %{"status" => "hey! @#{friend.nickname}"})
449
450 {:ok, activity_three, _} = CommonAPI.repeat(activity_two.id, friend)
451
452 activities =
453 ActivityPub.fetch_activities([], %{"blocking_user" => blocker})
454 |> Enum.map(fn act -> act.id end)
455
456 assert Enum.member?(activities, activity_one.id)
457 refute Enum.member?(activities, activity_two.id)
458 refute Enum.member?(activities, activity_three.id)
459 end
460
461 test "doesn't return activities from blocked domains" do
462 domain = "dogwhistle.zone"
463 domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"})
464 note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})
465 activity = insert(:note_activity, %{note: note})
466 user = insert(:user)
467 {:ok, user} = User.block_domain(user, domain)
468
469 activities =
470 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
471
472 refute activity in activities
473
474 followed_user = insert(:user)
475 ActivityPub.follow(user, followed_user)
476 {:ok, repeat_activity, _} = CommonAPI.repeat(activity.id, followed_user)
477
478 activities =
479 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
480
481 refute repeat_activity in activities
482 end
483
484 test "doesn't return muted activities" do
485 activity_one = insert(:note_activity)
486 activity_two = insert(:note_activity)
487 activity_three = insert(:note_activity)
488 user = insert(:user)
489 booster = insert(:user)
490 {:ok, user} = User.mute(user, %User{ap_id: activity_one.data["actor"]})
491
492 activities =
493 ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
494
495 assert Enum.member?(activities, activity_two)
496 assert Enum.member?(activities, activity_three)
497 refute Enum.member?(activities, activity_one)
498
499 # Calling with 'with_muted' will deliver muted activities, too.
500 activities =
501 ActivityPub.fetch_activities([], %{
502 "muting_user" => user,
503 "with_muted" => true,
504 "skip_preload" => true
505 })
506
507 assert Enum.member?(activities, activity_two)
508 assert Enum.member?(activities, activity_three)
509 assert Enum.member?(activities, activity_one)
510
511 {:ok, user} = User.unmute(user, %User{ap_id: activity_one.data["actor"]})
512
513 activities =
514 ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
515
516 assert Enum.member?(activities, activity_two)
517 assert Enum.member?(activities, activity_three)
518 assert Enum.member?(activities, activity_one)
519
520 {:ok, user} = User.mute(user, %User{ap_id: activity_three.data["actor"]})
521 {:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
522 %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
523 activity_three = Activity.get_by_id(activity_three.id)
524
525 activities =
526 ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
527
528 assert Enum.member?(activities, activity_two)
529 refute Enum.member?(activities, activity_three)
530 refute Enum.member?(activities, boost_activity)
531 assert Enum.member?(activities, activity_one)
532
533 activities = ActivityPub.fetch_activities([], %{"muting_user" => nil, "skip_preload" => true})
534
535 assert Enum.member?(activities, activity_two)
536 assert Enum.member?(activities, activity_three)
537 assert Enum.member?(activities, boost_activity)
538 assert Enum.member?(activities, activity_one)
539 end
540
541 test "does include announces on request" do
542 activity_three = insert(:note_activity)
543 user = insert(:user)
544 booster = insert(:user)
545
546 {:ok, user} = User.follow(user, booster)
547
548 {:ok, announce, _object} = CommonAPI.repeat(activity_three.id, booster)
549
550 [announce_activity] = ActivityPub.fetch_activities([user.ap_id | user.following])
551
552 assert announce_activity.id == announce.id
553 end
554
555 test "excludes reblogs on request" do
556 user = insert(:user)
557 {:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Create"}, %{:user => user})
558 {:ok, _} = ActivityBuilder.insert(%{"type" => "Announce"}, %{:user => user})
559
560 [activity] = ActivityPub.fetch_user_activities(user, nil, %{"exclude_reblogs" => "true"})
561
562 assert activity == expected_activity
563 end
564
565 describe "public fetch activities" do
566 test "doesn't retrieve unlisted activities" do
567 user = insert(:user)
568
569 {:ok, _unlisted_activity} =
570 CommonAPI.post(user, %{"status" => "yeah", "visibility" => "unlisted"})
571
572 {:ok, listed_activity} = CommonAPI.post(user, %{"status" => "yeah"})
573
574 [activity] = ActivityPub.fetch_public_activities()
575
576 assert activity == listed_activity
577 end
578
579 test "retrieves public activities" do
580 _activities = ActivityPub.fetch_public_activities()
581
582 %{public: public} = ActivityBuilder.public_and_non_public()
583
584 activities = ActivityPub.fetch_public_activities()
585 assert length(activities) == 1
586 assert Enum.at(activities, 0) == public
587 end
588
589 test "retrieves a maximum of 20 activities" do
590 activities = ActivityBuilder.insert_list(30)
591 last_expected = List.last(activities)
592
593 activities = ActivityPub.fetch_public_activities()
594 last = List.last(activities)
595
596 assert length(activities) == 20
597 assert last == last_expected
598 end
599
600 test "retrieves ids starting from a since_id" do
601 activities = ActivityBuilder.insert_list(30)
602 later_activities = ActivityBuilder.insert_list(10)
603 since_id = List.last(activities).id
604 last_expected = List.last(later_activities)
605
606 activities = ActivityPub.fetch_public_activities(%{"since_id" => since_id})
607 last = List.last(activities)
608
609 assert length(activities) == 10
610 assert last == last_expected
611 end
612
613 test "retrieves ids up to max_id" do
614 _first_activities = ActivityBuilder.insert_list(10)
615 activities = ActivityBuilder.insert_list(20)
616 later_activities = ActivityBuilder.insert_list(10)
617 max_id = List.first(later_activities).id
618 last_expected = List.last(activities)
619
620 activities = ActivityPub.fetch_public_activities(%{"max_id" => max_id})
621 last = List.last(activities)
622
623 assert length(activities) == 20
624 assert last == last_expected
625 end
626
627 test "doesn't return reblogs for users for whom reblogs have been muted" do
628 activity = insert(:note_activity)
629 user = insert(:user)
630 booster = insert(:user)
631 {:ok, user} = CommonAPI.hide_reblogs(user, booster)
632
633 {:ok, activity, _} = CommonAPI.repeat(activity.id, booster)
634
635 activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
636
637 refute Enum.any?(activities, fn %{id: id} -> id == activity.id end)
638 end
639
640 test "returns reblogs for users for whom reblogs have not been muted" do
641 activity = insert(:note_activity)
642 user = insert(:user)
643 booster = insert(:user)
644 {:ok, user} = CommonAPI.hide_reblogs(user, booster)
645 {:ok, user} = CommonAPI.show_reblogs(user, booster)
646
647 {:ok, activity, _} = CommonAPI.repeat(activity.id, booster)
648
649 activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
650
651 assert Enum.any?(activities, fn %{id: id} -> id == activity.id end)
652 end
653 end
654
655 describe "like an object" do
656 test "adds a like activity to the db" do
657 note_activity = insert(:note_activity)
658 assert object = Object.normalize(note_activity)
659
660 user = insert(:user)
661 user_two = insert(:user)
662
663 {:ok, like_activity, object} = ActivityPub.like(user, object)
664
665 assert like_activity.data["actor"] == user.ap_id
666 assert like_activity.data["type"] == "Like"
667 assert like_activity.data["object"] == object.data["id"]
668 assert like_activity.data["to"] == [User.ap_followers(user), note_activity.data["actor"]]
669 assert like_activity.data["context"] == object.data["context"]
670 assert object.data["like_count"] == 1
671 assert object.data["likes"] == [user.ap_id]
672
673 # Just return the original activity if the user already liked it.
674 {:ok, same_like_activity, object} = ActivityPub.like(user, object)
675
676 assert like_activity == same_like_activity
677 assert object.data["likes"] == [user.ap_id]
678 assert object.data["like_count"] == 1
679
680 {:ok, _like_activity, object} = ActivityPub.like(user_two, object)
681 assert object.data["like_count"] == 2
682 end
683 end
684
685 describe "unliking" do
686 test "unliking a previously liked object" do
687 note_activity = insert(:note_activity)
688 object = Object.normalize(note_activity)
689 user = insert(:user)
690
691 # Unliking something that hasn't been liked does nothing
692 {:ok, object} = ActivityPub.unlike(user, object)
693 assert object.data["like_count"] == 0
694
695 {:ok, like_activity, object} = ActivityPub.like(user, object)
696 assert object.data["like_count"] == 1
697
698 {:ok, _, _, object} = ActivityPub.unlike(user, object)
699 assert object.data["like_count"] == 0
700
701 assert Activity.get_by_id(like_activity.id) == nil
702 end
703 end
704
705 describe "announcing an object" do
706 test "adds an announce activity to the db" do
707 note_activity = insert(:note_activity)
708 object = Object.normalize(note_activity)
709 user = insert(:user)
710
711 {:ok, announce_activity, object} = ActivityPub.announce(user, object)
712 assert object.data["announcement_count"] == 1
713 assert object.data["announcements"] == [user.ap_id]
714
715 assert announce_activity.data["to"] == [
716 User.ap_followers(user),
717 note_activity.data["actor"]
718 ]
719
720 assert announce_activity.data["object"] == object.data["id"]
721 assert announce_activity.data["actor"] == user.ap_id
722 assert announce_activity.data["context"] == object.data["context"]
723 end
724 end
725
726 describe "unannouncing an object" do
727 test "unannouncing a previously announced object" do
728 note_activity = insert(:note_activity)
729 object = Object.normalize(note_activity)
730 user = insert(:user)
731
732 # Unannouncing an object that is not announced does nothing
733 # {:ok, object} = ActivityPub.unannounce(user, object)
734 # assert object.data["announcement_count"] == 0
735
736 {:ok, announce_activity, object} = ActivityPub.announce(user, object)
737 assert object.data["announcement_count"] == 1
738
739 {:ok, unannounce_activity, object} = ActivityPub.unannounce(user, object)
740 assert object.data["announcement_count"] == 0
741
742 assert unannounce_activity.data["to"] == [
743 User.ap_followers(user),
744 announce_activity.data["actor"]
745 ]
746
747 assert unannounce_activity.data["type"] == "Undo"
748 assert unannounce_activity.data["object"] == announce_activity.data
749 assert unannounce_activity.data["actor"] == user.ap_id
750 assert unannounce_activity.data["context"] == announce_activity.data["context"]
751
752 assert Activity.get_by_id(announce_activity.id) == nil
753 end
754 end
755
756 describe "uploading files" do
757 test "copies the file to the configured folder" do
758 file = %Plug.Upload{
759 content_type: "image/jpg",
760 path: Path.absname("test/fixtures/image.jpg"),
761 filename: "an_image.jpg"
762 }
763
764 {:ok, %Object{} = object} = ActivityPub.upload(file)
765 assert object.data["name"] == "an_image.jpg"
766 end
767
768 test "works with base64 encoded images" do
769 file = %{
770 "img" => data_uri()
771 }
772
773 {:ok, %Object{}} = ActivityPub.upload(file)
774 end
775 end
776
777 describe "fetch the latest Follow" do
778 test "fetches the latest Follow activity" do
779 %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
780 follower = Repo.get_by(User, ap_id: activity.data["actor"])
781 followed = Repo.get_by(User, ap_id: activity.data["object"])
782
783 assert activity == Utils.fetch_latest_follow(follower, followed)
784 end
785 end
786
787 describe "following / unfollowing" do
788 test "creates a follow activity" do
789 follower = insert(:user)
790 followed = insert(:user)
791
792 {:ok, activity} = ActivityPub.follow(follower, followed)
793 assert activity.data["type"] == "Follow"
794 assert activity.data["actor"] == follower.ap_id
795 assert activity.data["object"] == followed.ap_id
796 end
797
798 test "creates an undo activity for the last follow" do
799 follower = insert(:user)
800 followed = insert(:user)
801
802 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
803 {:ok, activity} = ActivityPub.unfollow(follower, followed)
804
805 assert activity.data["type"] == "Undo"
806 assert activity.data["actor"] == follower.ap_id
807
808 embedded_object = activity.data["object"]
809 assert is_map(embedded_object)
810 assert embedded_object["type"] == "Follow"
811 assert embedded_object["object"] == followed.ap_id
812 assert embedded_object["id"] == follow_activity.data["id"]
813 end
814 end
815
816 describe "blocking / unblocking" do
817 test "creates a block activity" do
818 blocker = insert(:user)
819 blocked = insert(:user)
820
821 {:ok, activity} = ActivityPub.block(blocker, blocked)
822
823 assert activity.data["type"] == "Block"
824 assert activity.data["actor"] == blocker.ap_id
825 assert activity.data["object"] == blocked.ap_id
826 end
827
828 test "creates an undo activity for the last block" do
829 blocker = insert(:user)
830 blocked = insert(:user)
831
832 {:ok, block_activity} = ActivityPub.block(blocker, blocked)
833 {:ok, activity} = ActivityPub.unblock(blocker, blocked)
834
835 assert activity.data["type"] == "Undo"
836 assert activity.data["actor"] == blocker.ap_id
837
838 embedded_object = activity.data["object"]
839 assert is_map(embedded_object)
840 assert embedded_object["type"] == "Block"
841 assert embedded_object["object"] == blocked.ap_id
842 assert embedded_object["id"] == block_activity.data["id"]
843 end
844 end
845
846 describe "deletion" do
847 test "it creates a delete activity and deletes the original object" do
848 note = insert(:note_activity)
849 object = Object.normalize(note)
850 {:ok, delete} = ActivityPub.delete(object)
851
852 assert delete.data["type"] == "Delete"
853 assert delete.data["actor"] == note.data["actor"]
854 assert delete.data["object"] == object.data["id"]
855
856 assert Activity.get_by_id(delete.id) != nil
857
858 assert Repo.get(Object, object.id).data["type"] == "Tombstone"
859 end
860
861 test "decrements user note count only for public activities" do
862 user = insert(:user, info: %{note_count: 10})
863
864 {:ok, a1} =
865 CommonAPI.post(User.get_cached_by_id(user.id), %{
866 "status" => "yeah",
867 "visibility" => "public"
868 })
869
870 {:ok, a2} =
871 CommonAPI.post(User.get_cached_by_id(user.id), %{
872 "status" => "yeah",
873 "visibility" => "unlisted"
874 })
875
876 {:ok, a3} =
877 CommonAPI.post(User.get_cached_by_id(user.id), %{
878 "status" => "yeah",
879 "visibility" => "private"
880 })
881
882 {:ok, a4} =
883 CommonAPI.post(User.get_cached_by_id(user.id), %{
884 "status" => "yeah",
885 "visibility" => "direct"
886 })
887
888 {:ok, _} = Object.normalize(a1) |> ActivityPub.delete()
889 {:ok, _} = Object.normalize(a2) |> ActivityPub.delete()
890 {:ok, _} = Object.normalize(a3) |> ActivityPub.delete()
891 {:ok, _} = Object.normalize(a4) |> ActivityPub.delete()
892
893 user = User.get_cached_by_id(user.id)
894 assert user.info.note_count == 10
895 end
896
897 test "it creates a delete activity and checks that it is also sent to users mentioned by the deleted object" do
898 user = insert(:user)
899 note = insert(:note_activity)
900 object = Object.normalize(note)
901
902 {:ok, object} =
903 object
904 |> Object.change(%{
905 data: %{
906 "actor" => object.data["actor"],
907 "id" => object.data["id"],
908 "to" => [user.ap_id],
909 "type" => "Note"
910 }
911 })
912 |> Object.update_and_set_cache()
913
914 {:ok, delete} = ActivityPub.delete(object)
915
916 assert user.ap_id in delete.data["to"]
917 end
918
919 test "decreases reply count" do
920 user = insert(:user)
921 user2 = insert(:user)
922
923 {:ok, activity} = CommonAPI.post(user, %{"status" => "1", "visibility" => "public"})
924 reply_data = %{"status" => "1", "in_reply_to_status_id" => activity.id}
925 ap_id = activity.data["id"]
926
927 {:ok, public_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "public"))
928 {:ok, unlisted_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "unlisted"))
929 {:ok, private_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "private"))
930 {:ok, direct_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct"))
931
932 _ = CommonAPI.delete(direct_reply.id, user2)
933 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
934 assert object.data["repliesCount"] == 2
935
936 _ = CommonAPI.delete(private_reply.id, user2)
937 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
938 assert object.data["repliesCount"] == 2
939
940 _ = CommonAPI.delete(public_reply.id, user2)
941 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
942 assert object.data["repliesCount"] == 1
943
944 _ = CommonAPI.delete(unlisted_reply.id, user2)
945 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
946 assert object.data["repliesCount"] == 0
947 end
948 end
949
950 describe "timeline post-processing" do
951 test "it filters broken threads" do
952 user1 = insert(:user)
953 user2 = insert(:user)
954 user3 = insert(:user)
955
956 {:ok, user1} = User.follow(user1, user3)
957 assert User.following?(user1, user3)
958
959 {:ok, user2} = User.follow(user2, user3)
960 assert User.following?(user2, user3)
961
962 {:ok, user3} = User.follow(user3, user2)
963 assert User.following?(user3, user2)
964
965 {:ok, public_activity} = CommonAPI.post(user3, %{"status" => "hi 1"})
966
967 {:ok, private_activity_1} =
968 CommonAPI.post(user3, %{"status" => "hi 2", "visibility" => "private"})
969
970 {:ok, private_activity_2} =
971 CommonAPI.post(user2, %{
972 "status" => "hi 3",
973 "visibility" => "private",
974 "in_reply_to_status_id" => private_activity_1.id
975 })
976
977 {:ok, private_activity_3} =
978 CommonAPI.post(user3, %{
979 "status" => "hi 4",
980 "visibility" => "private",
981 "in_reply_to_status_id" => private_activity_2.id
982 })
983
984 activities =
985 ActivityPub.fetch_activities([user1.ap_id | user1.following])
986 |> Enum.map(fn a -> a.id end)
987
988 private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"])
989
990 assert [public_activity.id, private_activity_1.id, private_activity_3.id] == activities
991
992 assert length(activities) == 3
993
994 activities =
995 ActivityPub.fetch_activities([user1.ap_id | user1.following], %{"user" => user1})
996 |> Enum.map(fn a -> a.id end)
997
998 assert [public_activity.id, private_activity_1.id] == activities
999 assert length(activities) == 2
1000 end
1001 end
1002
1003 describe "update" do
1004 test "it creates an update activity with the new user data" do
1005 user = insert(:user)
1006 {:ok, user} = User.ensure_keys_present(user)
1007 user_data = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
1008
1009 {:ok, update} =
1010 ActivityPub.update(%{
1011 actor: user_data["id"],
1012 to: [user.follower_address],
1013 cc: [],
1014 object: user_data
1015 })
1016
1017 assert update.data["actor"] == user.ap_id
1018 assert update.data["to"] == [user.follower_address]
1019 assert embedded_object = update.data["object"]
1020 assert embedded_object["id"] == user_data["id"]
1021 assert embedded_object["type"] == user_data["type"]
1022 end
1023 end
1024
1025 test "returned pinned statuses" do
1026 Pleroma.Config.put([:instance, :max_pinned_statuses], 3)
1027 user = insert(:user)
1028
1029 {:ok, activity_one} = CommonAPI.post(user, %{"status" => "HI!!!"})
1030 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
1031 {:ok, activity_three} = CommonAPI.post(user, %{"status" => "HI!!!"})
1032
1033 CommonAPI.pin(activity_one.id, user)
1034 user = refresh_record(user)
1035
1036 CommonAPI.pin(activity_two.id, user)
1037 user = refresh_record(user)
1038
1039 CommonAPI.pin(activity_three.id, user)
1040 user = refresh_record(user)
1041
1042 activities = ActivityPub.fetch_user_activities(user, nil, %{"pinned" => "true"})
1043
1044 assert 3 = length(activities)
1045 end
1046
1047 test "it can create a Flag activity" do
1048 reporter = insert(:user)
1049 target_account = insert(:user)
1050 {:ok, activity} = CommonAPI.post(target_account, %{"status" => "foobar"})
1051 context = Utils.generate_context_id()
1052 content = "foobar"
1053
1054 reporter_ap_id = reporter.ap_id
1055 target_ap_id = target_account.ap_id
1056 activity_ap_id = activity.data["id"]
1057
1058 assert {:ok, activity} =
1059 ActivityPub.flag(%{
1060 actor: reporter,
1061 context: context,
1062 account: target_account,
1063 statuses: [activity],
1064 content: content
1065 })
1066
1067 assert %Activity{
1068 actor: ^reporter_ap_id,
1069 data: %{
1070 "type" => "Flag",
1071 "content" => ^content,
1072 "context" => ^context,
1073 "object" => [^target_ap_id, ^activity_ap_id]
1074 }
1075 } = activity
1076 end
1077
1078 test "fetch_activities/2 returns activities addressed to a list " do
1079 user = insert(:user)
1080 member = insert(:user)
1081 {:ok, list} = Pleroma.List.create("foo", user)
1082 {:ok, list} = Pleroma.List.follow(list, member)
1083
1084 {:ok, activity} =
1085 CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"})
1086
1087 activity = Repo.preload(activity, :bookmark)
1088 activity = %Activity{activity | thread_muted?: !!activity.thread_muted?}
1089
1090 assert ActivityPub.fetch_activities([], %{"user" => user}) == [activity]
1091 end
1092
1093 def data_uri do
1094 File.read!("test/fixtures/avatar_data_uri")
1095 end
1096
1097 describe "fetch_activities_bounded" do
1098 test "fetches private posts for followed users" do
1099 user = insert(:user)
1100
1101 {:ok, activity} =
1102 CommonAPI.post(user, %{
1103 "status" => "thought I looked cute might delete later :3",
1104 "visibility" => "private"
1105 })
1106
1107 [result] = ActivityPub.fetch_activities_bounded([user.follower_address], [])
1108 assert result.id == activity.id
1109 end
1110
1111 test "fetches only public posts for other users" do
1112 user = insert(:user)
1113 {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe", "visibility" => "public"})
1114
1115 {:ok, _private_activity} =
1116 CommonAPI.post(user, %{
1117 "status" => "why is tenshi eating a corndog so cute?",
1118 "visibility" => "private"
1119 })
1120
1121 [result] = ActivityPub.fetch_activities_bounded([], [user.follower_address])
1122 assert result.id == activity.id
1123 end
1124 end
1125
1126 describe "fetch_follow_information_for_user" do
1127 test "syncronizes following/followers counters" do
1128 user =
1129 insert(:user,
1130 local: false,
1131 follower_address: "http://localhost:4001/users/fuser2/followers",
1132 following_address: "http://localhost:4001/users/fuser2/following"
1133 )
1134
1135 {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
1136 assert info.follower_count == 527
1137 assert info.following_count == 267
1138 end
1139
1140 test "detects hidden followers" do
1141 mock(fn env ->
1142 case env.url do
1143 "http://localhost:4001/users/masto_closed/followers?page=1" ->
1144 %Tesla.Env{status: 403, body: ""}
1145
1146 _ ->
1147 apply(HttpRequestMock, :request, [env])
1148 end
1149 end)
1150
1151 user =
1152 insert(:user,
1153 local: false,
1154 follower_address: "http://localhost:4001/users/masto_closed/followers",
1155 following_address: "http://localhost:4001/users/masto_closed/following"
1156 )
1157
1158 {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
1159 assert info.hide_followers == true
1160 assert info.hide_follows == false
1161 end
1162
1163 test "detects hidden follows" do
1164 mock(fn env ->
1165 case env.url do
1166 "http://localhost:4001/users/masto_closed/following?page=1" ->
1167 %Tesla.Env{status: 403, body: ""}
1168
1169 _ ->
1170 apply(HttpRequestMock, :request, [env])
1171 end
1172 end)
1173
1174 user =
1175 insert(:user,
1176 local: false,
1177 follower_address: "http://localhost:4001/users/masto_closed/followers",
1178 following_address: "http://localhost:4001/users/masto_closed/following"
1179 )
1180
1181 {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
1182 assert info.hide_followers == false
1183 assert info.hide_follows == true
1184 end
1185 end
1186 end