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