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