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