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