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