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