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