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