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