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