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