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