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