test: update obsolete test
[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 muted activities" do
466 activity_one = insert(:note_activity)
467 activity_two = insert(:note_activity)
468 activity_three = insert(:note_activity)
469 user = insert(:user)
470 booster = insert(:user)
471 {:ok, user} = User.mute(user, %User{ap_id: activity_one.data["actor"]})
472
473 activities =
474 ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
475
476 assert Enum.member?(activities, activity_two)
477 assert Enum.member?(activities, activity_three)
478 refute Enum.member?(activities, activity_one)
479
480 # Calling with 'with_muted' will deliver muted activities, too.
481 activities =
482 ActivityPub.fetch_activities([], %{
483 "muting_user" => user,
484 "with_muted" => true,
485 "skip_preload" => true
486 })
487
488 assert Enum.member?(activities, activity_two)
489 assert Enum.member?(activities, activity_three)
490 assert Enum.member?(activities, activity_one)
491
492 {:ok, user} = User.unmute(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 assert Enum.member?(activities, activity_one)
500
501 {:ok, user} = User.mute(user, %User{ap_id: activity_three.data["actor"]})
502 {:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
503 %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
504 activity_three = Activity.get_by_id(activity_three.id)
505
506 activities =
507 ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
508
509 assert Enum.member?(activities, activity_two)
510 refute Enum.member?(activities, activity_three)
511 refute Enum.member?(activities, boost_activity)
512 assert Enum.member?(activities, activity_one)
513
514 activities = ActivityPub.fetch_activities([], %{"muting_user" => nil, "skip_preload" => true})
515
516 assert Enum.member?(activities, activity_two)
517 assert Enum.member?(activities, activity_three)
518 assert Enum.member?(activities, boost_activity)
519 assert Enum.member?(activities, activity_one)
520 end
521
522 test "does include announces on request" do
523 activity_three = insert(:note_activity)
524 user = insert(:user)
525 booster = insert(:user)
526
527 {:ok, user} = User.follow(user, booster)
528
529 {:ok, announce, _object} = CommonAPI.repeat(activity_three.id, booster)
530
531 [announce_activity] = ActivityPub.fetch_activities([user.ap_id | user.following])
532
533 assert announce_activity.id == announce.id
534 end
535
536 test "excludes reblogs on request" do
537 user = insert(:user)
538 {:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Create"}, %{:user => user})
539 {:ok, _} = ActivityBuilder.insert(%{"type" => "Announce"}, %{:user => user})
540
541 [activity] = ActivityPub.fetch_user_activities(user, nil, %{"exclude_reblogs" => "true"})
542
543 assert activity == expected_activity
544 end
545
546 describe "public fetch activities" do
547 test "doesn't retrieve unlisted activities" do
548 user = insert(:user)
549
550 {:ok, _unlisted_activity} =
551 CommonAPI.post(user, %{"status" => "yeah", "visibility" => "unlisted"})
552
553 {:ok, listed_activity} = CommonAPI.post(user, %{"status" => "yeah"})
554
555 [activity] = ActivityPub.fetch_public_activities()
556
557 assert activity == listed_activity
558 end
559
560 test "retrieves public activities" do
561 _activities = ActivityPub.fetch_public_activities()
562
563 %{public: public} = ActivityBuilder.public_and_non_public()
564
565 activities = ActivityPub.fetch_public_activities()
566 assert length(activities) == 1
567 assert Enum.at(activities, 0) == public
568 end
569
570 test "retrieves a maximum of 20 activities" do
571 activities = ActivityBuilder.insert_list(30)
572 last_expected = List.last(activities)
573
574 activities = ActivityPub.fetch_public_activities()
575 last = List.last(activities)
576
577 assert length(activities) == 20
578 assert last == last_expected
579 end
580
581 test "retrieves ids starting from a since_id" do
582 activities = ActivityBuilder.insert_list(30)
583 later_activities = ActivityBuilder.insert_list(10)
584 since_id = List.last(activities).id
585 last_expected = List.last(later_activities)
586
587 activities = ActivityPub.fetch_public_activities(%{"since_id" => since_id})
588 last = List.last(activities)
589
590 assert length(activities) == 10
591 assert last == last_expected
592 end
593
594 test "retrieves ids up to max_id" do
595 _first_activities = ActivityBuilder.insert_list(10)
596 activities = ActivityBuilder.insert_list(20)
597 later_activities = ActivityBuilder.insert_list(10)
598 max_id = List.first(later_activities).id
599 last_expected = List.last(activities)
600
601 activities = ActivityPub.fetch_public_activities(%{"max_id" => max_id})
602 last = List.last(activities)
603
604 assert length(activities) == 20
605 assert last == last_expected
606 end
607
608 test "doesn't return reblogs for users for whom reblogs have been muted" do
609 activity = insert(:note_activity)
610 user = insert(:user)
611 booster = insert(:user)
612 {:ok, user} = CommonAPI.hide_reblogs(user, booster)
613
614 {:ok, activity, _} = CommonAPI.repeat(activity.id, booster)
615
616 activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
617
618 refute Enum.any?(activities, fn %{id: id} -> id == activity.id end)
619 end
620
621 test "returns reblogs for users for whom reblogs have not been muted" do
622 activity = insert(:note_activity)
623 user = insert(:user)
624 booster = insert(:user)
625 {:ok, user} = CommonAPI.hide_reblogs(user, booster)
626 {:ok, user} = CommonAPI.show_reblogs(user, booster)
627
628 {:ok, activity, _} = CommonAPI.repeat(activity.id, booster)
629
630 activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
631
632 assert Enum.any?(activities, fn %{id: id} -> id == activity.id end)
633 end
634 end
635
636 describe "like an object" do
637 test "adds a like activity to the db" do
638 note_activity = insert(:note_activity)
639 object = Object.get_by_ap_id(note_activity.data["object"]["id"])
640 user = insert(:user)
641 user_two = insert(:user)
642
643 {:ok, like_activity, object} = ActivityPub.like(user, object)
644
645 assert like_activity.data["actor"] == user.ap_id
646 assert like_activity.data["type"] == "Like"
647 assert like_activity.data["object"] == object.data["id"]
648 assert like_activity.data["to"] == [User.ap_followers(user), note_activity.data["actor"]]
649 assert like_activity.data["context"] == object.data["context"]
650 assert object.data["like_count"] == 1
651 assert object.data["likes"] == [user.ap_id]
652
653 # Just return the original activity if the user already liked it.
654 {:ok, same_like_activity, object} = ActivityPub.like(user, object)
655
656 assert like_activity == same_like_activity
657 assert object.data["likes"] == [user.ap_id]
658
659 [note_activity] = Activity.get_all_create_by_object_ap_id(object.data["id"])
660 assert note_activity.data["object"]["like_count"] == 1
661
662 {:ok, _like_activity, object} = ActivityPub.like(user_two, object)
663 assert object.data["like_count"] == 2
664 end
665 end
666
667 describe "unliking" do
668 test "unliking a previously liked object" do
669 note_activity = insert(:note_activity)
670 object = Object.get_by_ap_id(note_activity.data["object"]["id"])
671 user = insert(:user)
672
673 # Unliking something that hasn't been liked does nothing
674 {:ok, object} = ActivityPub.unlike(user, object)
675 assert object.data["like_count"] == 0
676
677 {:ok, like_activity, object} = ActivityPub.like(user, object)
678 assert object.data["like_count"] == 1
679
680 {:ok, _, _, object} = ActivityPub.unlike(user, object)
681 assert object.data["like_count"] == 0
682
683 assert Activity.get_by_id(like_activity.id) == nil
684 end
685 end
686
687 describe "announcing an object" do
688 test "adds an announce activity to the db" do
689 note_activity = insert(:note_activity)
690 object = Object.get_by_ap_id(note_activity.data["object"]["id"])
691 user = insert(:user)
692
693 {:ok, announce_activity, object} = ActivityPub.announce(user, object)
694 assert object.data["announcement_count"] == 1
695 assert object.data["announcements"] == [user.ap_id]
696
697 assert announce_activity.data["to"] == [
698 User.ap_followers(user),
699 note_activity.data["actor"]
700 ]
701
702 assert announce_activity.data["object"] == object.data["id"]
703 assert announce_activity.data["actor"] == user.ap_id
704 assert announce_activity.data["context"] == object.data["context"]
705 end
706 end
707
708 describe "unannouncing an object" do
709 test "unannouncing a previously announced object" do
710 note_activity = insert(:note_activity)
711 object = Object.get_by_ap_id(note_activity.data["object"]["id"])
712 user = insert(:user)
713
714 # Unannouncing an object that is not announced does nothing
715 # {:ok, object} = ActivityPub.unannounce(user, object)
716 # assert object.data["announcement_count"] == 0
717
718 {:ok, announce_activity, object} = ActivityPub.announce(user, object)
719 assert object.data["announcement_count"] == 1
720
721 {:ok, unannounce_activity, object} = ActivityPub.unannounce(user, object)
722 assert object.data["announcement_count"] == 0
723
724 assert unannounce_activity.data["to"] == [
725 User.ap_followers(user),
726 announce_activity.data["actor"]
727 ]
728
729 assert unannounce_activity.data["type"] == "Undo"
730 assert unannounce_activity.data["object"] == announce_activity.data
731 assert unannounce_activity.data["actor"] == user.ap_id
732 assert unannounce_activity.data["context"] == announce_activity.data["context"]
733
734 assert Activity.get_by_id(announce_activity.id) == nil
735 end
736 end
737
738 describe "uploading files" do
739 test "copies the file to the configured folder" do
740 file = %Plug.Upload{
741 content_type: "image/jpg",
742 path: Path.absname("test/fixtures/image.jpg"),
743 filename: "an_image.jpg"
744 }
745
746 {:ok, %Object{} = object} = ActivityPub.upload(file)
747 assert object.data["name"] == "an_image.jpg"
748 end
749
750 test "works with base64 encoded images" do
751 file = %{
752 "img" => data_uri()
753 }
754
755 {:ok, %Object{}} = ActivityPub.upload(file)
756 end
757 end
758
759 describe "fetch the latest Follow" do
760 test "fetches the latest Follow activity" do
761 %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
762 follower = Repo.get_by(User, ap_id: activity.data["actor"])
763 followed = Repo.get_by(User, ap_id: activity.data["object"])
764
765 assert activity == Utils.fetch_latest_follow(follower, followed)
766 end
767 end
768
769 describe "following / unfollowing" do
770 test "creates a follow activity" do
771 follower = insert(:user)
772 followed = insert(:user)
773
774 {:ok, activity} = ActivityPub.follow(follower, followed)
775 assert activity.data["type"] == "Follow"
776 assert activity.data["actor"] == follower.ap_id
777 assert activity.data["object"] == followed.ap_id
778 end
779
780 test "creates an undo activity for the last follow" do
781 follower = insert(:user)
782 followed = insert(:user)
783
784 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
785 {:ok, activity} = ActivityPub.unfollow(follower, followed)
786
787 assert activity.data["type"] == "Undo"
788 assert activity.data["actor"] == follower.ap_id
789
790 assert is_map(activity.data["object"])
791 assert activity.data["object"]["type"] == "Follow"
792 assert activity.data["object"]["object"] == followed.ap_id
793 assert activity.data["object"]["id"] == follow_activity.data["id"]
794 end
795 end
796
797 describe "blocking / unblocking" do
798 test "creates a block activity" do
799 blocker = insert(:user)
800 blocked = insert(:user)
801
802 {:ok, activity} = ActivityPub.block(blocker, blocked)
803
804 assert activity.data["type"] == "Block"
805 assert activity.data["actor"] == blocker.ap_id
806 assert activity.data["object"] == blocked.ap_id
807 end
808
809 test "creates an undo activity for the last block" do
810 blocker = insert(:user)
811 blocked = insert(:user)
812
813 {:ok, block_activity} = ActivityPub.block(blocker, blocked)
814 {:ok, activity} = ActivityPub.unblock(blocker, blocked)
815
816 assert activity.data["type"] == "Undo"
817 assert activity.data["actor"] == blocker.ap_id
818
819 assert is_map(activity.data["object"])
820 assert activity.data["object"]["type"] == "Block"
821 assert activity.data["object"]["object"] == blocked.ap_id
822 assert activity.data["object"]["id"] == block_activity.data["id"]
823 end
824 end
825
826 describe "deletion" do
827 test "it creates a delete activity and deletes the original object" do
828 note = insert(:note_activity)
829 object = Object.get_by_ap_id(note.data["object"]["id"])
830 {:ok, delete} = ActivityPub.delete(object)
831
832 assert delete.data["type"] == "Delete"
833 assert delete.data["actor"] == note.data["actor"]
834 assert delete.data["object"] == note.data["object"]["id"]
835
836 assert Activity.get_by_id(delete.id) != nil
837
838 assert Repo.get(Object, object.id).data["type"] == "Tombstone"
839 end
840
841 test "decrements user note count only for public activities" do
842 user = insert(:user, info: %{note_count: 10})
843
844 {:ok, a1} =
845 CommonAPI.post(User.get_cached_by_id(user.id), %{
846 "status" => "yeah",
847 "visibility" => "public"
848 })
849
850 {:ok, a2} =
851 CommonAPI.post(User.get_cached_by_id(user.id), %{
852 "status" => "yeah",
853 "visibility" => "unlisted"
854 })
855
856 {:ok, a3} =
857 CommonAPI.post(User.get_cached_by_id(user.id), %{
858 "status" => "yeah",
859 "visibility" => "private"
860 })
861
862 {:ok, a4} =
863 CommonAPI.post(User.get_cached_by_id(user.id), %{
864 "status" => "yeah",
865 "visibility" => "direct"
866 })
867
868 {:ok, _} = Object.normalize(a1) |> ActivityPub.delete()
869 {:ok, _} = Object.normalize(a2) |> ActivityPub.delete()
870 {:ok, _} = Object.normalize(a3) |> ActivityPub.delete()
871 {:ok, _} = Object.normalize(a4) |> ActivityPub.delete()
872
873 user = User.get_cached_by_id(user.id)
874 assert user.info.note_count == 10
875 end
876
877 test "it creates a delete activity and checks that it is also sent to users mentioned by the deleted object" do
878 user = insert(:user)
879 note = insert(:note_activity)
880
881 {:ok, object} =
882 Object.get_by_ap_id(note.data["object"]["id"])
883 |> Object.change(%{
884 data: %{
885 "actor" => note.data["object"]["actor"],
886 "id" => note.data["object"]["id"],
887 "to" => [user.ap_id],
888 "type" => "Note"
889 }
890 })
891 |> Object.update_and_set_cache()
892
893 {:ok, delete} = ActivityPub.delete(object)
894
895 assert user.ap_id in delete.data["to"]
896 end
897
898 test "decreases reply count" do
899 user = insert(:user)
900 user2 = insert(:user)
901
902 {:ok, activity} = CommonAPI.post(user, %{"status" => "1", "visibility" => "public"})
903 reply_data = %{"status" => "1", "in_reply_to_status_id" => activity.id}
904 ap_id = activity.data["id"]
905
906 {:ok, public_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "public"))
907 {:ok, unlisted_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "unlisted"))
908 {:ok, private_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "private"))
909 {:ok, direct_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct"))
910
911 _ = CommonAPI.delete(direct_reply.id, user2)
912 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
913 assert object.data["repliesCount"] == 2
914
915 _ = CommonAPI.delete(private_reply.id, user2)
916 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
917 assert object.data["repliesCount"] == 2
918
919 _ = CommonAPI.delete(public_reply.id, user2)
920 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
921 assert object.data["repliesCount"] == 1
922
923 _ = CommonAPI.delete(unlisted_reply.id, user2)
924 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
925 assert object.data["repliesCount"] == 0
926 end
927 end
928
929 describe "timeline post-processing" do
930 test "it filters broken threads" do
931 user1 = insert(:user)
932 user2 = insert(:user)
933 user3 = insert(:user)
934
935 {:ok, user1} = User.follow(user1, user3)
936 assert User.following?(user1, user3)
937
938 {:ok, user2} = User.follow(user2, user3)
939 assert User.following?(user2, user3)
940
941 {:ok, user3} = User.follow(user3, user2)
942 assert User.following?(user3, user2)
943
944 {:ok, public_activity} = CommonAPI.post(user3, %{"status" => "hi 1"})
945
946 {:ok, private_activity_1} =
947 CommonAPI.post(user3, %{"status" => "hi 2", "visibility" => "private"})
948
949 {:ok, private_activity_2} =
950 CommonAPI.post(user2, %{
951 "status" => "hi 3",
952 "visibility" => "private",
953 "in_reply_to_status_id" => private_activity_1.id
954 })
955
956 {:ok, private_activity_3} =
957 CommonAPI.post(user3, %{
958 "status" => "hi 4",
959 "visibility" => "private",
960 "in_reply_to_status_id" => private_activity_2.id
961 })
962
963 activities = ActivityPub.fetch_activities([user1.ap_id | user1.following])
964
965 private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"])
966
967 assert [public_activity, private_activity_1, private_activity_3] == activities
968
969 assert length(activities) == 3
970
971 activities =
972 ActivityPub.fetch_activities([user1.ap_id | user1.following], %{"user" => user1})
973
974 assert [public_activity, private_activity_1] == activities
975 assert length(activities) == 2
976 end
977 end
978
979 describe "update" do
980 test "it creates an update activity with the new user data" do
981 user = insert(:user)
982 {:ok, user} = Pleroma.Web.WebFinger.ensure_keys_present(user)
983 user_data = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
984
985 {:ok, update} =
986 ActivityPub.update(%{
987 actor: user_data["id"],
988 to: [user.follower_address],
989 cc: [],
990 object: user_data
991 })
992
993 assert update.data["actor"] == user.ap_id
994 assert update.data["to"] == [user.follower_address]
995 assert update.data["object"]["id"] == user_data["id"]
996 assert update.data["object"]["type"] == user_data["type"]
997 end
998 end
999
1000 test "returned pinned statuses" do
1001 Pleroma.Config.put([:instance, :max_pinned_statuses], 3)
1002 user = insert(:user)
1003
1004 {:ok, activity_one} = CommonAPI.post(user, %{"status" => "HI!!!"})
1005 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
1006 {:ok, activity_three} = CommonAPI.post(user, %{"status" => "HI!!!"})
1007
1008 CommonAPI.pin(activity_one.id, user)
1009 user = refresh_record(user)
1010
1011 CommonAPI.pin(activity_two.id, user)
1012 user = refresh_record(user)
1013
1014 CommonAPI.pin(activity_three.id, user)
1015 user = refresh_record(user)
1016
1017 activities = ActivityPub.fetch_user_activities(user, nil, %{"pinned" => "true"})
1018
1019 assert 3 = length(activities)
1020 end
1021
1022 test "it can create a Flag activity" do
1023 reporter = insert(:user)
1024 target_account = insert(:user)
1025 {:ok, activity} = CommonAPI.post(target_account, %{"status" => "foobar"})
1026 context = Utils.generate_context_id()
1027 content = "foobar"
1028
1029 reporter_ap_id = reporter.ap_id
1030 target_ap_id = target_account.ap_id
1031 activity_ap_id = activity.data["id"]
1032
1033 assert {:ok, activity} =
1034 ActivityPub.flag(%{
1035 actor: reporter,
1036 context: context,
1037 account: target_account,
1038 statuses: [activity],
1039 content: content
1040 })
1041
1042 assert %Activity{
1043 actor: ^reporter_ap_id,
1044 data: %{
1045 "type" => "Flag",
1046 "content" => ^content,
1047 "context" => ^context,
1048 "object" => [^target_ap_id, ^activity_ap_id]
1049 }
1050 } = activity
1051 end
1052
1053 describe "publish_one/1" do
1054 test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is not specified",
1055 Instances,
1056 [:passthrough],
1057 [] do
1058 actor = insert(:user)
1059 inbox = "http://200.site/users/nick1/inbox"
1060
1061 assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
1062
1063 assert called(Instances.set_reachable(inbox))
1064 end
1065
1066 test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is set",
1067 Instances,
1068 [:passthrough],
1069 [] do
1070 actor = insert(:user)
1071 inbox = "http://200.site/users/nick1/inbox"
1072
1073 assert {:ok, _} =
1074 Publisher.publish_one(%{
1075 inbox: inbox,
1076 json: "{}",
1077 actor: actor,
1078 id: 1,
1079 unreachable_since: NaiveDateTime.utc_now()
1080 })
1081
1082 assert called(Instances.set_reachable(inbox))
1083 end
1084
1085 test_with_mock "does NOT call `Instances.set_reachable` on successful federation if `unreachable_since` is nil",
1086 Instances,
1087 [:passthrough],
1088 [] do
1089 actor = insert(:user)
1090 inbox = "http://200.site/users/nick1/inbox"
1091
1092 assert {:ok, _} =
1093 Publisher.publish_one(%{
1094 inbox: inbox,
1095 json: "{}",
1096 actor: actor,
1097 id: 1,
1098 unreachable_since: nil
1099 })
1100
1101 refute called(Instances.set_reachable(inbox))
1102 end
1103
1104 test_with_mock "calls `Instances.set_unreachable` on target inbox on non-2xx HTTP response code",
1105 Instances,
1106 [:passthrough],
1107 [] do
1108 actor = insert(:user)
1109 inbox = "http://404.site/users/nick1/inbox"
1110
1111 assert {:error, _} = Publisher.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, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
1124
1125 assert called(Instances.set_unreachable(inbox))
1126 end
1127
1128 test_with_mock "does NOT call `Instances.set_unreachable` if target is reachable",
1129 Instances,
1130 [:passthrough],
1131 [] do
1132 actor = insert(:user)
1133 inbox = "http://200.site/users/nick1/inbox"
1134
1135 assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
1136
1137 refute called(Instances.set_unreachable(inbox))
1138 end
1139
1140 test_with_mock "does NOT call `Instances.set_unreachable` if target instance has non-nil `unreachable_since`",
1141 Instances,
1142 [:passthrough],
1143 [] do
1144 actor = insert(:user)
1145 inbox = "http://connrefused.site/users/nick1/inbox"
1146
1147 assert {:error, _} =
1148 Publisher.publish_one(%{
1149 inbox: inbox,
1150 json: "{}",
1151 actor: actor,
1152 id: 1,
1153 unreachable_since: NaiveDateTime.utc_now()
1154 })
1155
1156 refute called(Instances.set_unreachable(inbox))
1157 end
1158 end
1159
1160 def data_uri do
1161 File.read!("test/fixtures/avatar_data_uri")
1162 end
1163 end