Revert "Merge branch 'revert-4fabf83a' into 'develop'"
[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))
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))
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, _, _, object} = ActivityPub.unlike(user, object)
764 assert object.data["like_count"] == 0
765
766 assert Activity.get_by_id(like_activity.id) == nil
767 end
768 end
769
770 describe "announcing an object" do
771 test "adds an announce activity to the db" do
772 note_activity = insert(:note_activity)
773 object = Object.normalize(note_activity)
774 user = insert(:user)
775
776 {:ok, announce_activity, object} = ActivityPub.announce(user, object)
777 assert object.data["announcement_count"] == 1
778 assert object.data["announcements"] == [user.ap_id]
779
780 assert announce_activity.data["to"] == [
781 User.ap_followers(user),
782 note_activity.data["actor"]
783 ]
784
785 assert announce_activity.data["object"] == object.data["id"]
786 assert announce_activity.data["actor"] == user.ap_id
787 assert announce_activity.data["context"] == object.data["context"]
788 end
789 end
790
791 describe "unannouncing an object" do
792 test "unannouncing a previously announced object" do
793 note_activity = insert(:note_activity)
794 object = Object.normalize(note_activity)
795 user = insert(:user)
796
797 # Unannouncing an object that is not announced does nothing
798 # {:ok, object} = ActivityPub.unannounce(user, object)
799 # assert object.data["announcement_count"] == 0
800
801 {:ok, announce_activity, object} = ActivityPub.announce(user, object)
802 assert object.data["announcement_count"] == 1
803
804 {:ok, unannounce_activity, object} = ActivityPub.unannounce(user, object)
805 assert object.data["announcement_count"] == 0
806
807 assert unannounce_activity.data["to"] == [
808 User.ap_followers(user),
809 announce_activity.data["actor"]
810 ]
811
812 assert unannounce_activity.data["type"] == "Undo"
813 assert unannounce_activity.data["object"] == announce_activity.data
814 assert unannounce_activity.data["actor"] == user.ap_id
815 assert unannounce_activity.data["context"] == announce_activity.data["context"]
816
817 assert Activity.get_by_id(announce_activity.id) == nil
818 end
819 end
820
821 describe "uploading files" do
822 test "copies the file to the configured folder" do
823 file = %Plug.Upload{
824 content_type: "image/jpg",
825 path: Path.absname("test/fixtures/image.jpg"),
826 filename: "an_image.jpg"
827 }
828
829 {:ok, %Object{} = object} = ActivityPub.upload(file)
830 assert object.data["name"] == "an_image.jpg"
831 end
832
833 test "works with base64 encoded images" do
834 file = %{
835 "img" => data_uri()
836 }
837
838 {:ok, %Object{}} = ActivityPub.upload(file)
839 end
840 end
841
842 describe "fetch the latest Follow" do
843 test "fetches the latest Follow activity" do
844 %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
845 follower = Repo.get_by(User, ap_id: activity.data["actor"])
846 followed = Repo.get_by(User, ap_id: activity.data["object"])
847
848 assert activity == Utils.fetch_latest_follow(follower, followed)
849 end
850 end
851
852 describe "following / unfollowing" do
853 test "creates a follow activity" do
854 follower = insert(:user)
855 followed = insert(:user)
856
857 {:ok, activity} = ActivityPub.follow(follower, followed)
858 assert activity.data["type"] == "Follow"
859 assert activity.data["actor"] == follower.ap_id
860 assert activity.data["object"] == followed.ap_id
861 end
862
863 test "creates an undo activity for the last follow" do
864 follower = insert(:user)
865 followed = insert(:user)
866
867 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
868 {:ok, activity} = ActivityPub.unfollow(follower, followed)
869
870 assert activity.data["type"] == "Undo"
871 assert activity.data["actor"] == follower.ap_id
872
873 embedded_object = activity.data["object"]
874 assert is_map(embedded_object)
875 assert embedded_object["type"] == "Follow"
876 assert embedded_object["object"] == followed.ap_id
877 assert embedded_object["id"] == follow_activity.data["id"]
878 end
879 end
880
881 describe "blocking / unblocking" do
882 test "creates a block activity" do
883 blocker = insert(:user)
884 blocked = insert(:user)
885
886 {:ok, activity} = ActivityPub.block(blocker, blocked)
887
888 assert activity.data["type"] == "Block"
889 assert activity.data["actor"] == blocker.ap_id
890 assert activity.data["object"] == blocked.ap_id
891 end
892
893 test "creates an undo activity for the last block" do
894 blocker = insert(:user)
895 blocked = insert(:user)
896
897 {:ok, block_activity} = ActivityPub.block(blocker, blocked)
898 {:ok, activity} = ActivityPub.unblock(blocker, blocked)
899
900 assert activity.data["type"] == "Undo"
901 assert activity.data["actor"] == blocker.ap_id
902
903 embedded_object = activity.data["object"]
904 assert is_map(embedded_object)
905 assert embedded_object["type"] == "Block"
906 assert embedded_object["object"] == blocked.ap_id
907 assert embedded_object["id"] == block_activity.data["id"]
908 end
909 end
910
911 describe "deletion" do
912 test "it creates a delete activity and deletes the original object" do
913 note = insert(:note_activity)
914 object = Object.normalize(note)
915 {:ok, delete} = ActivityPub.delete(object)
916
917 assert delete.data["type"] == "Delete"
918 assert delete.data["actor"] == note.data["actor"]
919 assert delete.data["object"] == object.data["id"]
920
921 assert Activity.get_by_id(delete.id) != nil
922
923 assert Repo.get(Object, object.id).data["type"] == "Tombstone"
924 end
925
926 test "decrements user note count only for public activities" do
927 user = insert(:user, info: %{note_count: 10})
928
929 {:ok, a1} =
930 CommonAPI.post(User.get_cached_by_id(user.id), %{
931 "status" => "yeah",
932 "visibility" => "public"
933 })
934
935 {:ok, a2} =
936 CommonAPI.post(User.get_cached_by_id(user.id), %{
937 "status" => "yeah",
938 "visibility" => "unlisted"
939 })
940
941 {:ok, a3} =
942 CommonAPI.post(User.get_cached_by_id(user.id), %{
943 "status" => "yeah",
944 "visibility" => "private"
945 })
946
947 {:ok, a4} =
948 CommonAPI.post(User.get_cached_by_id(user.id), %{
949 "status" => "yeah",
950 "visibility" => "direct"
951 })
952
953 {:ok, _} = Object.normalize(a1) |> ActivityPub.delete()
954 {:ok, _} = Object.normalize(a2) |> ActivityPub.delete()
955 {:ok, _} = Object.normalize(a3) |> ActivityPub.delete()
956 {:ok, _} = Object.normalize(a4) |> ActivityPub.delete()
957
958 user = User.get_cached_by_id(user.id)
959 assert user.info.note_count == 10
960 end
961
962 test "it creates a delete activity and checks that it is also sent to users mentioned by the deleted object" do
963 user = insert(:user)
964 note = insert(:note_activity)
965 object = Object.normalize(note)
966
967 {:ok, object} =
968 object
969 |> Object.change(%{
970 data: %{
971 "actor" => object.data["actor"],
972 "id" => object.data["id"],
973 "to" => [user.ap_id],
974 "type" => "Note"
975 }
976 })
977 |> Object.update_and_set_cache()
978
979 {:ok, delete} = ActivityPub.delete(object)
980
981 assert user.ap_id in delete.data["to"]
982 end
983
984 test "decreases reply count" do
985 user = insert(:user)
986 user2 = insert(:user)
987
988 {:ok, activity} = CommonAPI.post(user, %{"status" => "1", "visibility" => "public"})
989 reply_data = %{"status" => "1", "in_reply_to_status_id" => activity.id}
990 ap_id = activity.data["id"]
991
992 {:ok, public_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "public"))
993 {:ok, unlisted_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "unlisted"))
994 {:ok, private_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "private"))
995 {:ok, direct_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct"))
996
997 _ = CommonAPI.delete(direct_reply.id, user2)
998 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
999 assert object.data["repliesCount"] == 2
1000
1001 _ = CommonAPI.delete(private_reply.id, user2)
1002 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1003 assert object.data["repliesCount"] == 2
1004
1005 _ = CommonAPI.delete(public_reply.id, user2)
1006 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1007 assert object.data["repliesCount"] == 1
1008
1009 _ = CommonAPI.delete(unlisted_reply.id, user2)
1010 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1011 assert object.data["repliesCount"] == 0
1012 end
1013 end
1014
1015 describe "timeline post-processing" do
1016 test "it filters broken threads" do
1017 user1 = insert(:user)
1018 user2 = insert(:user)
1019 user3 = insert(:user)
1020
1021 {:ok, user1} = User.follow(user1, user3)
1022 assert User.following?(user1, user3)
1023
1024 {:ok, user2} = User.follow(user2, user3)
1025 assert User.following?(user2, user3)
1026
1027 {:ok, user3} = User.follow(user3, user2)
1028 assert User.following?(user3, user2)
1029
1030 {:ok, public_activity} = CommonAPI.post(user3, %{"status" => "hi 1"})
1031
1032 {:ok, private_activity_1} =
1033 CommonAPI.post(user3, %{"status" => "hi 2", "visibility" => "private"})
1034
1035 {:ok, private_activity_2} =
1036 CommonAPI.post(user2, %{
1037 "status" => "hi 3",
1038 "visibility" => "private",
1039 "in_reply_to_status_id" => private_activity_1.id
1040 })
1041
1042 {:ok, private_activity_3} =
1043 CommonAPI.post(user3, %{
1044 "status" => "hi 4",
1045 "visibility" => "private",
1046 "in_reply_to_status_id" => private_activity_2.id
1047 })
1048
1049 activities =
1050 ActivityPub.fetch_activities([user1.ap_id | user1.following])
1051 |> Enum.map(fn a -> a.id end)
1052
1053 private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"])
1054
1055 assert [public_activity.id, private_activity_1.id, private_activity_3.id] == activities
1056
1057 assert length(activities) == 3
1058
1059 activities =
1060 ActivityPub.fetch_activities([user1.ap_id | user1.following], %{"user" => user1})
1061 |> Enum.map(fn a -> a.id end)
1062
1063 assert [public_activity.id, private_activity_1.id] == activities
1064 assert length(activities) == 2
1065 end
1066 end
1067
1068 describe "update" do
1069 test "it creates an update activity with the new user data" do
1070 user = insert(:user)
1071 {:ok, user} = User.ensure_keys_present(user)
1072 user_data = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
1073
1074 {:ok, update} =
1075 ActivityPub.update(%{
1076 actor: user_data["id"],
1077 to: [user.follower_address],
1078 cc: [],
1079 object: user_data
1080 })
1081
1082 assert update.data["actor"] == user.ap_id
1083 assert update.data["to"] == [user.follower_address]
1084 assert embedded_object = update.data["object"]
1085 assert embedded_object["id"] == user_data["id"]
1086 assert embedded_object["type"] == user_data["type"]
1087 end
1088 end
1089
1090 test "returned pinned statuses" do
1091 Pleroma.Config.put([:instance, :max_pinned_statuses], 3)
1092 user = insert(:user)
1093
1094 {:ok, activity_one} = CommonAPI.post(user, %{"status" => "HI!!!"})
1095 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
1096 {:ok, activity_three} = CommonAPI.post(user, %{"status" => "HI!!!"})
1097
1098 CommonAPI.pin(activity_one.id, user)
1099 user = refresh_record(user)
1100
1101 CommonAPI.pin(activity_two.id, user)
1102 user = refresh_record(user)
1103
1104 CommonAPI.pin(activity_three.id, user)
1105 user = refresh_record(user)
1106
1107 activities = ActivityPub.fetch_user_activities(user, nil, %{"pinned" => "true"})
1108
1109 assert 3 = length(activities)
1110 end
1111
1112 test "it can create a Flag activity" do
1113 reporter = insert(:user)
1114 target_account = insert(:user)
1115 {:ok, activity} = CommonAPI.post(target_account, %{"status" => "foobar"})
1116 context = Utils.generate_context_id()
1117 content = "foobar"
1118
1119 reporter_ap_id = reporter.ap_id
1120 target_ap_id = target_account.ap_id
1121 activity_ap_id = activity.data["id"]
1122
1123 assert {:ok, activity} =
1124 ActivityPub.flag(%{
1125 actor: reporter,
1126 context: context,
1127 account: target_account,
1128 statuses: [activity],
1129 content: content
1130 })
1131
1132 assert %Activity{
1133 actor: ^reporter_ap_id,
1134 data: %{
1135 "type" => "Flag",
1136 "content" => ^content,
1137 "context" => ^context,
1138 "object" => [^target_ap_id, ^activity_ap_id]
1139 }
1140 } = activity
1141 end
1142
1143 test "fetch_activities/2 returns activities addressed to a list " do
1144 user = insert(:user)
1145 member = insert(:user)
1146 {:ok, list} = Pleroma.List.create("foo", user)
1147 {:ok, list} = Pleroma.List.follow(list, member)
1148
1149 {:ok, activity} =
1150 CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"})
1151
1152 activity = Repo.preload(activity, :bookmark)
1153 activity = %Activity{activity | thread_muted?: !!activity.thread_muted?}
1154
1155 assert ActivityPub.fetch_activities([], %{"user" => user}) == [activity]
1156 end
1157
1158 def data_uri do
1159 File.read!("test/fixtures/avatar_data_uri")
1160 end
1161
1162 describe "fetch_activities_bounded" do
1163 test "fetches private posts for followed users" do
1164 user = insert(:user)
1165
1166 {:ok, activity} =
1167 CommonAPI.post(user, %{
1168 "status" => "thought I looked cute might delete later :3",
1169 "visibility" => "private"
1170 })
1171
1172 [result] = ActivityPub.fetch_activities_bounded([user.follower_address], [])
1173 assert result.id == activity.id
1174 end
1175
1176 test "fetches only public posts for other users" do
1177 user = insert(:user)
1178 {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe", "visibility" => "public"})
1179
1180 {:ok, _private_activity} =
1181 CommonAPI.post(user, %{
1182 "status" => "why is tenshi eating a corndog so cute?",
1183 "visibility" => "private"
1184 })
1185
1186 [result] = ActivityPub.fetch_activities_bounded([], [user.follower_address])
1187 assert result.id == activity.id
1188 end
1189 end
1190
1191 describe "fetch_follow_information_for_user" do
1192 test "syncronizes following/followers counters" do
1193 user =
1194 insert(:user,
1195 local: false,
1196 follower_address: "http://localhost:4001/users/fuser2/followers",
1197 following_address: "http://localhost:4001/users/fuser2/following"
1198 )
1199
1200 {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
1201 assert info.follower_count == 527
1202 assert info.following_count == 267
1203 end
1204
1205 test "detects hidden followers" do
1206 mock(fn env ->
1207 case env.url do
1208 "http://localhost:4001/users/masto_closed/followers?page=1" ->
1209 %Tesla.Env{status: 403, body: ""}
1210
1211 _ ->
1212 apply(HttpRequestMock, :request, [env])
1213 end
1214 end)
1215
1216 user =
1217 insert(:user,
1218 local: false,
1219 follower_address: "http://localhost:4001/users/masto_closed/followers",
1220 following_address: "http://localhost:4001/users/masto_closed/following"
1221 )
1222
1223 {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
1224 assert info.hide_followers == true
1225 assert info.hide_follows == false
1226 end
1227
1228 test "detects hidden follows" do
1229 mock(fn env ->
1230 case env.url do
1231 "http://localhost:4001/users/masto_closed/following?page=1" ->
1232 %Tesla.Env{status: 403, body: ""}
1233
1234 _ ->
1235 apply(HttpRequestMock, :request, [env])
1236 end
1237 end)
1238
1239 user =
1240 insert(:user,
1241 local: false,
1242 follower_address: "http://localhost:4001/users/masto_closed/followers",
1243 following_address: "http://localhost:4001/users/masto_closed/following"
1244 )
1245
1246 {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
1247 assert info.hide_followers == false
1248 assert info.hide_follows == true
1249 end
1250 end
1251 end