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