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