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