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