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