Merge branch 'unblock-domain-via-query' into 'develop'
[akkoma] / test / web / activity_pub / activity_pub_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 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 use Oban.Testing, repo: Pleroma.Repo
8
9 alias Pleroma.Activity
10 alias Pleroma.Builders.ActivityBuilder
11 alias Pleroma.Config
12 alias Pleroma.Notification
13 alias Pleroma.Object
14 alias Pleroma.User
15 alias Pleroma.Web.ActivityPub.ActivityPub
16 alias Pleroma.Web.ActivityPub.Utils
17 alias Pleroma.Web.AdminAPI.AccountView
18 alias Pleroma.Web.CommonAPI
19
20 import ExUnit.CaptureLog
21 import Mock
22 import Pleroma.Factory
23 import Tesla.Mock
24
25 setup do
26 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
27 :ok
28 end
29
30 setup do: clear_config([:instance, :federating])
31
32 describe "streaming out participations" do
33 test "it streams them out" do
34 user = insert(:user)
35 {:ok, activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
36
37 {:ok, conversation} = Pleroma.Conversation.create_or_bump_for(activity)
38
39 participations =
40 conversation.participations
41 |> Repo.preload(:user)
42
43 with_mock Pleroma.Web.Streamer,
44 stream: fn _, _ -> nil end do
45 ActivityPub.stream_out_participations(conversation.participations)
46
47 assert called(Pleroma.Web.Streamer.stream("participation", participations))
48 end
49 end
50
51 test "streams them out on activity creation" do
52 user_one = insert(:user)
53 user_two = insert(:user)
54
55 with_mock Pleroma.Web.Streamer,
56 stream: fn _, _ -> nil end do
57 {:ok, activity} =
58 CommonAPI.post(user_one, %{
59 status: "@#{user_two.nickname}",
60 visibility: "direct"
61 })
62
63 conversation =
64 activity.data["context"]
65 |> Pleroma.Conversation.get_for_ap_id()
66 |> Repo.preload(participations: :user)
67
68 assert called(Pleroma.Web.Streamer.stream("participation", conversation.participations))
69 end
70 end
71 end
72
73 describe "fetching restricted by visibility" do
74 test "it restricts by the appropriate visibility" do
75 user = insert(:user)
76
77 {:ok, public_activity} = CommonAPI.post(user, %{status: ".", visibility: "public"})
78
79 {:ok, direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
80
81 {:ok, unlisted_activity} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"})
82
83 {:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
84
85 activities = ActivityPub.fetch_activities([], %{visibility: "direct", actor_id: user.ap_id})
86
87 assert activities == [direct_activity]
88
89 activities =
90 ActivityPub.fetch_activities([], %{visibility: "unlisted", actor_id: user.ap_id})
91
92 assert activities == [unlisted_activity]
93
94 activities =
95 ActivityPub.fetch_activities([], %{visibility: "private", actor_id: user.ap_id})
96
97 assert activities == [private_activity]
98
99 activities = ActivityPub.fetch_activities([], %{visibility: "public", actor_id: user.ap_id})
100
101 assert activities == [public_activity]
102
103 activities =
104 ActivityPub.fetch_activities([], %{
105 visibility: ~w[private public],
106 actor_id: user.ap_id
107 })
108
109 assert activities == [public_activity, private_activity]
110 end
111 end
112
113 describe "fetching excluded by visibility" do
114 test "it excludes by the appropriate visibility" do
115 user = insert(:user)
116
117 {:ok, public_activity} = CommonAPI.post(user, %{status: ".", visibility: "public"})
118
119 {:ok, direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
120
121 {:ok, unlisted_activity} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"})
122
123 {:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
124
125 activities =
126 ActivityPub.fetch_activities([], %{
127 exclude_visibilities: "direct",
128 actor_id: user.ap_id
129 })
130
131 assert public_activity in activities
132 assert unlisted_activity in activities
133 assert private_activity in activities
134 refute direct_activity in activities
135
136 activities =
137 ActivityPub.fetch_activities([], %{
138 exclude_visibilities: "unlisted",
139 actor_id: user.ap_id
140 })
141
142 assert public_activity in activities
143 refute unlisted_activity in activities
144 assert private_activity in activities
145 assert direct_activity in activities
146
147 activities =
148 ActivityPub.fetch_activities([], %{
149 exclude_visibilities: "private",
150 actor_id: user.ap_id
151 })
152
153 assert public_activity in activities
154 assert unlisted_activity in activities
155 refute private_activity in activities
156 assert direct_activity in activities
157
158 activities =
159 ActivityPub.fetch_activities([], %{
160 exclude_visibilities: "public",
161 actor_id: user.ap_id
162 })
163
164 refute public_activity in activities
165 assert unlisted_activity in activities
166 assert private_activity in activities
167 assert direct_activity in activities
168 end
169 end
170
171 describe "building a user from his ap id" do
172 test "it returns a user" do
173 user_id = "http://mastodon.example.org/users/admin"
174 {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
175 assert user.ap_id == user_id
176 assert user.nickname == "admin@mastodon.example.org"
177 assert user.ap_enabled
178 assert user.follower_address == "http://mastodon.example.org/users/admin/followers"
179 end
180
181 test "it returns a user that is invisible" do
182 user_id = "http://mastodon.example.org/users/relay"
183 {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
184 assert User.invisible?(user)
185 end
186
187 test "it returns a user that accepts chat messages" do
188 user_id = "http://mastodon.example.org/users/admin"
189 {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
190
191 assert user.accepts_chat_messages
192 end
193 end
194
195 test "it fetches the appropriate tag-restricted posts" do
196 user = insert(:user)
197
198 {:ok, status_one} = CommonAPI.post(user, %{status: ". #test"})
199 {:ok, status_two} = CommonAPI.post(user, %{status: ". #essais"})
200 {:ok, status_three} = CommonAPI.post(user, %{status: ". #test #reject"})
201
202 fetch_one = ActivityPub.fetch_activities([], %{type: "Create", tag: "test"})
203
204 fetch_two = ActivityPub.fetch_activities([], %{type: "Create", tag: ["test", "essais"]})
205
206 fetch_three =
207 ActivityPub.fetch_activities([], %{
208 type: "Create",
209 tag: ["test", "essais"],
210 tag_reject: ["reject"]
211 })
212
213 fetch_four =
214 ActivityPub.fetch_activities([], %{
215 type: "Create",
216 tag: ["test"],
217 tag_all: ["test", "reject"]
218 })
219
220 assert fetch_one == [status_one, status_three]
221 assert fetch_two == [status_one, status_two, status_three]
222 assert fetch_three == [status_one, status_two]
223 assert fetch_four == [status_three]
224 end
225
226 describe "insertion" do
227 test "drops activities beyond a certain limit" do
228 limit = Config.get([:instance, :remote_limit])
229
230 random_text =
231 :crypto.strong_rand_bytes(limit + 1)
232 |> Base.encode64()
233 |> binary_part(0, limit + 1)
234
235 data = %{
236 "ok" => true,
237 "object" => %{
238 "content" => random_text
239 }
240 }
241
242 assert {:error, {:remote_limit_error, _}} = ActivityPub.insert(data)
243 end
244
245 test "doesn't drop activities with content being null" do
246 user = insert(:user)
247
248 data = %{
249 "actor" => user.ap_id,
250 "to" => [],
251 "object" => %{
252 "actor" => user.ap_id,
253 "to" => [],
254 "type" => "Note",
255 "content" => nil
256 }
257 }
258
259 assert {:ok, _} = ActivityPub.insert(data)
260 end
261
262 test "returns the activity if one with the same id is already in" do
263 activity = insert(:note_activity)
264 {:ok, new_activity} = ActivityPub.insert(activity.data)
265
266 assert activity.id == new_activity.id
267 end
268
269 test "inserts a given map into the activity database, giving it an id if it has none." do
270 user = insert(:user)
271
272 data = %{
273 "actor" => user.ap_id,
274 "to" => [],
275 "object" => %{
276 "actor" => user.ap_id,
277 "to" => [],
278 "type" => "Note",
279 "content" => "hey"
280 }
281 }
282
283 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
284 assert activity.data["ok"] == data["ok"]
285 assert is_binary(activity.data["id"])
286
287 given_id = "bla"
288
289 data = %{
290 "id" => given_id,
291 "actor" => user.ap_id,
292 "to" => [],
293 "context" => "blabla",
294 "object" => %{
295 "actor" => user.ap_id,
296 "to" => [],
297 "type" => "Note",
298 "content" => "hey"
299 }
300 }
301
302 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
303 assert activity.data["ok"] == data["ok"]
304 assert activity.data["id"] == given_id
305 assert activity.data["context"] == "blabla"
306 assert activity.data["context_id"]
307 end
308
309 test "adds a context when none is there" do
310 user = insert(:user)
311
312 data = %{
313 "actor" => user.ap_id,
314 "to" => [],
315 "object" => %{
316 "actor" => user.ap_id,
317 "to" => [],
318 "type" => "Note",
319 "content" => "hey"
320 }
321 }
322
323 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
324 object = Pleroma.Object.normalize(activity)
325
326 assert is_binary(activity.data["context"])
327 assert is_binary(object.data["context"])
328 assert activity.data["context_id"]
329 assert object.data["context_id"]
330 end
331
332 test "adds an id to a given object if it lacks one and is a note and inserts it to the object database" do
333 user = insert(:user)
334
335 data = %{
336 "actor" => user.ap_id,
337 "to" => [],
338 "object" => %{
339 "actor" => user.ap_id,
340 "to" => [],
341 "type" => "Note",
342 "content" => "hey"
343 }
344 }
345
346 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
347 assert object = Object.normalize(activity)
348 assert is_binary(object.data["id"])
349 end
350 end
351
352 describe "listen activities" do
353 test "does not increase user note count" do
354 user = insert(:user)
355
356 {:ok, activity} =
357 ActivityPub.listen(%{
358 to: ["https://www.w3.org/ns/activitystreams#Public"],
359 actor: user,
360 context: "",
361 object: %{
362 "actor" => user.ap_id,
363 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
364 "artist" => "lain",
365 "title" => "lain radio episode 1",
366 "length" => 180_000,
367 "type" => "Audio"
368 }
369 })
370
371 assert activity.actor == user.ap_id
372
373 user = User.get_cached_by_id(user.id)
374 assert user.note_count == 0
375 end
376
377 test "can be fetched into a timeline" do
378 _listen_activity_1 = insert(:listen)
379 _listen_activity_2 = insert(:listen)
380 _listen_activity_3 = insert(:listen)
381
382 timeline = ActivityPub.fetch_activities([], %{type: ["Listen"]})
383
384 assert length(timeline) == 3
385 end
386 end
387
388 describe "create activities" do
389 test "it reverts create" do
390 user = insert(:user)
391
392 with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
393 assert {:error, :reverted} =
394 ActivityPub.create(%{
395 to: ["user1", "user2"],
396 actor: user,
397 context: "",
398 object: %{
399 "to" => ["user1", "user2"],
400 "type" => "Note",
401 "content" => "testing"
402 }
403 })
404 end
405
406 assert Repo.aggregate(Activity, :count, :id) == 0
407 assert Repo.aggregate(Object, :count, :id) == 0
408 end
409
410 test "removes doubled 'to' recipients" do
411 user = insert(:user)
412
413 {:ok, activity} =
414 ActivityPub.create(%{
415 to: ["user1", "user1", "user2"],
416 actor: user,
417 context: "",
418 object: %{
419 "to" => ["user1", "user1", "user2"],
420 "type" => "Note",
421 "content" => "testing"
422 }
423 })
424
425 assert activity.data["to"] == ["user1", "user2"]
426 assert activity.actor == user.ap_id
427 assert activity.recipients == ["user1", "user2", user.ap_id]
428 end
429
430 test "increases user note count only for public activities" do
431 user = insert(:user)
432
433 {:ok, _} =
434 CommonAPI.post(User.get_cached_by_id(user.id), %{
435 status: "1",
436 visibility: "public"
437 })
438
439 {:ok, _} =
440 CommonAPI.post(User.get_cached_by_id(user.id), %{
441 status: "2",
442 visibility: "unlisted"
443 })
444
445 {:ok, _} =
446 CommonAPI.post(User.get_cached_by_id(user.id), %{
447 status: "2",
448 visibility: "private"
449 })
450
451 {:ok, _} =
452 CommonAPI.post(User.get_cached_by_id(user.id), %{
453 status: "3",
454 visibility: "direct"
455 })
456
457 user = User.get_cached_by_id(user.id)
458 assert user.note_count == 2
459 end
460
461 test "increases replies count" do
462 user = insert(:user)
463 user2 = insert(:user)
464
465 {:ok, activity} = CommonAPI.post(user, %{status: "1", visibility: "public"})
466 ap_id = activity.data["id"]
467 reply_data = %{status: "1", in_reply_to_status_id: activity.id}
468
469 # public
470 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "public"))
471 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
472 assert object.data["repliesCount"] == 1
473
474 # unlisted
475 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "unlisted"))
476 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
477 assert object.data["repliesCount"] == 2
478
479 # private
480 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "private"))
481 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
482 assert object.data["repliesCount"] == 2
483
484 # direct
485 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "direct"))
486 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
487 assert object.data["repliesCount"] == 2
488 end
489 end
490
491 describe "fetch activities for recipients" do
492 test "retrieve the activities for certain recipients" do
493 {:ok, activity_one} = ActivityBuilder.insert(%{"to" => ["someone"]})
494 {:ok, activity_two} = ActivityBuilder.insert(%{"to" => ["someone_else"]})
495 {:ok, _activity_three} = ActivityBuilder.insert(%{"to" => ["noone"]})
496
497 activities = ActivityPub.fetch_activities(["someone", "someone_else"])
498 assert length(activities) == 2
499 assert activities == [activity_one, activity_two]
500 end
501 end
502
503 describe "fetch activities in context" do
504 test "retrieves activities that have a given context" do
505 {:ok, activity} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
506 {:ok, activity_two} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
507 {:ok, _activity_three} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"})
508 {:ok, _activity_four} = ActivityBuilder.insert(%{"type" => "Announce", "context" => "2hu"})
509 activity_five = insert(:note_activity)
510 user = insert(:user)
511
512 {:ok, _user_relationship} = User.block(user, %{ap_id: activity_five.data["actor"]})
513
514 activities = ActivityPub.fetch_activities_for_context("2hu", %{blocking_user: user})
515 assert activities == [activity_two, activity]
516 end
517
518 test "doesn't return activities with filtered words" do
519 user = insert(:user)
520 user_two = insert(:user)
521 insert(:filter, user: user, phrase: "test", hide: true)
522
523 {:ok, %{id: id1, data: %{"context" => context}}} = CommonAPI.post(user, %{status: "1"})
524
525 {:ok, %{id: id2}} = CommonAPI.post(user_two, %{status: "2", in_reply_to_status_id: id1})
526
527 {:ok, %{id: id3} = user_activity} =
528 CommonAPI.post(user, %{status: "3 test?", in_reply_to_status_id: id2})
529
530 {:ok, %{id: id4} = filtered_activity} =
531 CommonAPI.post(user_two, %{status: "4 test!", in_reply_to_status_id: id3})
532
533 {:ok, _} = CommonAPI.post(user, %{status: "5", in_reply_to_status_id: id4})
534
535 activities =
536 context
537 |> ActivityPub.fetch_activities_for_context(%{user: user})
538 |> Enum.map(& &1.id)
539
540 assert length(activities) == 4
541 assert user_activity.id in activities
542 refute filtered_activity.id in activities
543 end
544 end
545
546 test "doesn't return blocked activities" do
547 activity_one = insert(:note_activity)
548 activity_two = insert(:note_activity)
549 activity_three = insert(:note_activity)
550 user = insert(:user)
551 booster = insert(:user)
552 {:ok, _user_relationship} = User.block(user, %{ap_id: activity_one.data["actor"]})
553
554 activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
555
556 assert Enum.member?(activities, activity_two)
557 assert Enum.member?(activities, activity_three)
558 refute Enum.member?(activities, activity_one)
559
560 {:ok, _user_block} = User.unblock(user, %{ap_id: activity_one.data["actor"]})
561
562 activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
563
564 assert Enum.member?(activities, activity_two)
565 assert Enum.member?(activities, activity_three)
566 assert Enum.member?(activities, activity_one)
567
568 {:ok, _user_relationship} = User.block(user, %{ap_id: activity_three.data["actor"]})
569 {:ok, %{data: %{"object" => id}}} = CommonAPI.repeat(activity_three.id, booster)
570 %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
571 activity_three = Activity.get_by_id(activity_three.id)
572
573 activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
574
575 assert Enum.member?(activities, activity_two)
576 refute Enum.member?(activities, activity_three)
577 refute Enum.member?(activities, boost_activity)
578 assert Enum.member?(activities, activity_one)
579
580 activities = ActivityPub.fetch_activities([], %{blocking_user: nil, skip_preload: true})
581
582 assert Enum.member?(activities, activity_two)
583 assert Enum.member?(activities, activity_three)
584 assert Enum.member?(activities, boost_activity)
585 assert Enum.member?(activities, activity_one)
586 end
587
588 test "doesn't return transitive interactions concerning blocked users" do
589 blocker = insert(:user)
590 blockee = insert(:user)
591 friend = insert(:user)
592
593 {:ok, _user_relationship} = User.block(blocker, blockee)
594
595 {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey!"})
596
597 {:ok, activity_two} = CommonAPI.post(friend, %{status: "hey! @#{blockee.nickname}"})
598
599 {:ok, activity_three} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})
600
601 {:ok, activity_four} = CommonAPI.post(blockee, %{status: "hey! @#{blocker.nickname}"})
602
603 activities = ActivityPub.fetch_activities([], %{blocking_user: blocker})
604
605 assert Enum.member?(activities, activity_one)
606 refute Enum.member?(activities, activity_two)
607 refute Enum.member?(activities, activity_three)
608 refute Enum.member?(activities, activity_four)
609 end
610
611 test "doesn't return announce activities with blocked users in 'to'" do
612 blocker = insert(:user)
613 blockee = insert(:user)
614 friend = insert(:user)
615
616 {:ok, _user_relationship} = User.block(blocker, blockee)
617
618 {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey!"})
619
620 {:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})
621
622 {:ok, activity_three} = CommonAPI.repeat(activity_two.id, friend)
623
624 activities =
625 ActivityPub.fetch_activities([], %{blocking_user: blocker})
626 |> Enum.map(fn act -> act.id end)
627
628 assert Enum.member?(activities, activity_one.id)
629 refute Enum.member?(activities, activity_two.id)
630 refute Enum.member?(activities, activity_three.id)
631 end
632
633 test "doesn't return announce activities with blocked users in 'cc'" do
634 blocker = insert(:user)
635 blockee = insert(:user)
636 friend = insert(:user)
637
638 {:ok, _user_relationship} = User.block(blocker, blockee)
639
640 {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey!"})
641
642 {:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})
643
644 assert object = Pleroma.Object.normalize(activity_two)
645
646 data = %{
647 "actor" => friend.ap_id,
648 "object" => object.data["id"],
649 "context" => object.data["context"],
650 "type" => "Announce",
651 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
652 "cc" => [blockee.ap_id]
653 }
654
655 assert {:ok, activity_three} = ActivityPub.insert(data)
656
657 activities =
658 ActivityPub.fetch_activities([], %{blocking_user: blocker})
659 |> Enum.map(fn act -> act.id end)
660
661 assert Enum.member?(activities, activity_one.id)
662 refute Enum.member?(activities, activity_two.id)
663 refute Enum.member?(activities, activity_three.id)
664 end
665
666 test "doesn't return activities from blocked domains" do
667 domain = "dogwhistle.zone"
668 domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"})
669 note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})
670 activity = insert(:note_activity, %{note: note})
671 user = insert(:user)
672 {:ok, user} = User.block_domain(user, domain)
673
674 activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
675
676 refute activity in activities
677
678 followed_user = insert(:user)
679 CommonAPI.follow(user, followed_user)
680 {:ok, repeat_activity} = CommonAPI.repeat(activity.id, followed_user)
681
682 activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
683
684 refute repeat_activity in activities
685 end
686
687 test "does return activities from followed users on blocked domains" do
688 domain = "meanies.social"
689 domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"})
690 blocker = insert(:user)
691
692 {:ok, blocker} = User.follow(blocker, domain_user)
693 {:ok, blocker} = User.block_domain(blocker, domain)
694
695 assert User.following?(blocker, domain_user)
696 assert User.blocks_domain?(blocker, domain_user)
697 refute User.blocks?(blocker, domain_user)
698
699 note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})
700 activity = insert(:note_activity, %{note: note})
701
702 activities = ActivityPub.fetch_activities([], %{blocking_user: blocker, skip_preload: true})
703
704 assert activity in activities
705
706 # And check that if the guy we DO follow boosts someone else from their domain,
707 # that should be hidden
708 another_user = insert(:user, %{ap_id: "https://#{domain}/@meanie2"})
709 bad_note = insert(:note, %{data: %{"actor" => another_user.ap_id}})
710 bad_activity = insert(:note_activity, %{note: bad_note})
711 {:ok, repeat_activity} = CommonAPI.repeat(bad_activity.id, domain_user)
712
713 activities = ActivityPub.fetch_activities([], %{blocking_user: blocker, skip_preload: true})
714
715 refute repeat_activity in activities
716 end
717
718 test "doesn't return muted activities" do
719 activity_one = insert(:note_activity)
720 activity_two = insert(:note_activity)
721 activity_three = insert(:note_activity)
722 user = insert(:user)
723 booster = insert(:user)
724
725 activity_one_actor = User.get_by_ap_id(activity_one.data["actor"])
726 {:ok, _user_relationships} = User.mute(user, activity_one_actor)
727
728 activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
729
730 assert Enum.member?(activities, activity_two)
731 assert Enum.member?(activities, activity_three)
732 refute Enum.member?(activities, activity_one)
733
734 # Calling with 'with_muted' will deliver muted activities, too.
735 activities =
736 ActivityPub.fetch_activities([], %{
737 muting_user: user,
738 with_muted: true,
739 skip_preload: true
740 })
741
742 assert Enum.member?(activities, activity_two)
743 assert Enum.member?(activities, activity_three)
744 assert Enum.member?(activities, activity_one)
745
746 {:ok, _user_mute} = User.unmute(user, activity_one_actor)
747
748 activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
749
750 assert Enum.member?(activities, activity_two)
751 assert Enum.member?(activities, activity_three)
752 assert Enum.member?(activities, activity_one)
753
754 activity_three_actor = User.get_by_ap_id(activity_three.data["actor"])
755 {:ok, _user_relationships} = User.mute(user, activity_three_actor)
756 {:ok, %{data: %{"object" => id}}} = CommonAPI.repeat(activity_three.id, booster)
757 %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
758 activity_three = Activity.get_by_id(activity_three.id)
759
760 activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
761
762 assert Enum.member?(activities, activity_two)
763 refute Enum.member?(activities, activity_three)
764 refute Enum.member?(activities, boost_activity)
765 assert Enum.member?(activities, activity_one)
766
767 activities = ActivityPub.fetch_activities([], %{muting_user: nil, skip_preload: true})
768
769 assert Enum.member?(activities, activity_two)
770 assert Enum.member?(activities, activity_three)
771 assert Enum.member?(activities, boost_activity)
772 assert Enum.member?(activities, activity_one)
773 end
774
775 test "doesn't return thread muted activities" do
776 user = insert(:user)
777 _activity_one = insert(:note_activity)
778 note_two = insert(:note, data: %{"context" => "suya.."})
779 activity_two = insert(:note_activity, note: note_two)
780
781 {:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
782
783 assert [_activity_one] = ActivityPub.fetch_activities([], %{muting_user: user})
784 end
785
786 test "returns thread muted activities when with_muted is set" do
787 user = insert(:user)
788 _activity_one = insert(:note_activity)
789 note_two = insert(:note, data: %{"context" => "suya.."})
790 activity_two = insert(:note_activity, note: note_two)
791
792 {:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
793
794 assert [_activity_two, _activity_one] =
795 ActivityPub.fetch_activities([], %{muting_user: user, with_muted: true})
796 end
797
798 test "does include announces on request" do
799 activity_three = insert(:note_activity)
800 user = insert(:user)
801 booster = insert(:user)
802
803 {:ok, user} = User.follow(user, booster)
804
805 {:ok, announce} = CommonAPI.repeat(activity_three.id, booster)
806
807 [announce_activity] = ActivityPub.fetch_activities([user.ap_id | User.following(user)])
808
809 assert announce_activity.id == announce.id
810 end
811
812 test "excludes reblogs on request" do
813 user = insert(:user)
814 {:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Create"}, %{:user => user})
815 {:ok, _} = ActivityBuilder.insert(%{"type" => "Announce"}, %{:user => user})
816
817 [activity] = ActivityPub.fetch_user_activities(user, nil, %{exclude_reblogs: true})
818
819 assert activity == expected_activity
820 end
821
822 describe "irreversible filters" do
823 setup do
824 user = insert(:user)
825 user_two = insert(:user)
826
827 insert(:filter, user: user_two, phrase: "cofe", hide: true)
828 insert(:filter, user: user_two, phrase: "ok boomer", hide: true)
829 insert(:filter, user: user_two, phrase: "test", hide: false)
830
831 params = %{
832 type: ["Create", "Announce"],
833 user: user_two
834 }
835
836 {:ok, %{user: user, user_two: user_two, params: params}}
837 end
838
839 test "it returns statuses if they don't contain exact filter words", %{
840 user: user,
841 params: params
842 } do
843 {:ok, _} = CommonAPI.post(user, %{status: "hey"})
844 {:ok, _} = CommonAPI.post(user, %{status: "got cofefe?"})
845 {:ok, _} = CommonAPI.post(user, %{status: "I am not a boomer"})
846 {:ok, _} = CommonAPI.post(user, %{status: "ok boomers"})
847 {:ok, _} = CommonAPI.post(user, %{status: "ccofee is not a word"})
848 {:ok, _} = CommonAPI.post(user, %{status: "this is a test"})
849
850 activities = ActivityPub.fetch_activities([], params)
851
852 assert Enum.count(activities) == 6
853 end
854
855 test "it does not filter user's own statuses", %{user_two: user_two, params: params} do
856 {:ok, _} = CommonAPI.post(user_two, %{status: "Give me some cofe!"})
857 {:ok, _} = CommonAPI.post(user_two, %{status: "ok boomer"})
858
859 activities = ActivityPub.fetch_activities([], params)
860
861 assert Enum.count(activities) == 2
862 end
863
864 test "it excludes statuses with filter words", %{user: user, params: params} do
865 {:ok, _} = CommonAPI.post(user, %{status: "Give me some cofe!"})
866 {:ok, _} = CommonAPI.post(user, %{status: "ok boomer"})
867 {:ok, _} = CommonAPI.post(user, %{status: "is it a cOfE?"})
868 {:ok, _} = CommonAPI.post(user, %{status: "cofe is all I need"})
869 {:ok, _} = CommonAPI.post(user, %{status: "— ok BOOMER\n"})
870
871 activities = ActivityPub.fetch_activities([], params)
872
873 assert Enum.empty?(activities)
874 end
875
876 test "it returns all statuses if user does not have any filters" do
877 another_user = insert(:user)
878 {:ok, _} = CommonAPI.post(another_user, %{status: "got cofe?"})
879 {:ok, _} = CommonAPI.post(another_user, %{status: "test!"})
880
881 activities =
882 ActivityPub.fetch_activities([], %{
883 type: ["Create", "Announce"],
884 user: another_user
885 })
886
887 assert Enum.count(activities) == 2
888 end
889 end
890
891 describe "public fetch activities" do
892 test "doesn't retrieve unlisted activities" do
893 user = insert(:user)
894
895 {:ok, _unlisted_activity} = CommonAPI.post(user, %{status: "yeah", visibility: "unlisted"})
896
897 {:ok, listed_activity} = CommonAPI.post(user, %{status: "yeah"})
898
899 [activity] = ActivityPub.fetch_public_activities()
900
901 assert activity == listed_activity
902 end
903
904 test "retrieves public activities" do
905 _activities = ActivityPub.fetch_public_activities()
906
907 %{public: public} = ActivityBuilder.public_and_non_public()
908
909 activities = ActivityPub.fetch_public_activities()
910 assert length(activities) == 1
911 assert Enum.at(activities, 0) == public
912 end
913
914 test "retrieves a maximum of 20 activities" do
915 ActivityBuilder.insert_list(10)
916 expected_activities = ActivityBuilder.insert_list(20)
917
918 activities = ActivityPub.fetch_public_activities()
919
920 assert collect_ids(activities) == collect_ids(expected_activities)
921 assert length(activities) == 20
922 end
923
924 test "retrieves ids starting from a since_id" do
925 activities = ActivityBuilder.insert_list(30)
926 expected_activities = ActivityBuilder.insert_list(10)
927 since_id = List.last(activities).id
928
929 activities = ActivityPub.fetch_public_activities(%{since_id: since_id})
930
931 assert collect_ids(activities) == collect_ids(expected_activities)
932 assert length(activities) == 10
933 end
934
935 test "retrieves ids up to max_id" do
936 ActivityBuilder.insert_list(10)
937 expected_activities = ActivityBuilder.insert_list(20)
938
939 %{id: max_id} =
940 10
941 |> ActivityBuilder.insert_list()
942 |> List.first()
943
944 activities = ActivityPub.fetch_public_activities(%{max_id: max_id})
945
946 assert length(activities) == 20
947 assert collect_ids(activities) == collect_ids(expected_activities)
948 end
949
950 test "paginates via offset/limit" do
951 _first_part_activities = ActivityBuilder.insert_list(10)
952 second_part_activities = ActivityBuilder.insert_list(10)
953
954 later_activities = ActivityBuilder.insert_list(10)
955
956 activities = ActivityPub.fetch_public_activities(%{page: "2", page_size: "20"}, :offset)
957
958 assert length(activities) == 20
959
960 assert collect_ids(activities) ==
961 collect_ids(second_part_activities) ++ collect_ids(later_activities)
962 end
963
964 test "doesn't return reblogs for users for whom reblogs have been muted" do
965 activity = insert(:note_activity)
966 user = insert(:user)
967 booster = insert(:user)
968 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)
969
970 {:ok, activity} = CommonAPI.repeat(activity.id, booster)
971
972 activities = ActivityPub.fetch_activities([], %{muting_user: user})
973
974 refute Enum.any?(activities, fn %{id: id} -> id == activity.id end)
975 end
976
977 test "returns reblogs for users for whom reblogs have not been muted" do
978 activity = insert(:note_activity)
979 user = insert(:user)
980 booster = insert(:user)
981 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)
982 {:ok, _reblog_mute} = CommonAPI.show_reblogs(user, booster)
983
984 {:ok, activity} = CommonAPI.repeat(activity.id, booster)
985
986 activities = ActivityPub.fetch_activities([], %{muting_user: user})
987
988 assert Enum.any?(activities, fn %{id: id} -> id == activity.id end)
989 end
990 end
991
992 describe "uploading files" do
993 test "copies the file to the configured folder" do
994 file = %Plug.Upload{
995 content_type: "image/jpg",
996 path: Path.absname("test/fixtures/image.jpg"),
997 filename: "an_image.jpg"
998 }
999
1000 {:ok, %Object{} = object} = ActivityPub.upload(file)
1001 assert object.data["name"] == "an_image.jpg"
1002 end
1003
1004 test "works with base64 encoded images" do
1005 file = %{
1006 img: data_uri()
1007 }
1008
1009 {:ok, %Object{}} = ActivityPub.upload(file)
1010 end
1011 end
1012
1013 describe "fetch the latest Follow" do
1014 test "fetches the latest Follow activity" do
1015 %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
1016 follower = Repo.get_by(User, ap_id: activity.data["actor"])
1017 followed = Repo.get_by(User, ap_id: activity.data["object"])
1018
1019 assert activity == Utils.fetch_latest_follow(follower, followed)
1020 end
1021 end
1022
1023 describe "unfollowing" do
1024 test "it reverts unfollow activity" do
1025 follower = insert(:user)
1026 followed = insert(:user)
1027
1028 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed)
1029
1030 with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
1031 assert {:error, :reverted} = ActivityPub.unfollow(follower, followed)
1032 end
1033
1034 activity = Activity.get_by_id(follow_activity.id)
1035 assert activity.data["type"] == "Follow"
1036 assert activity.data["actor"] == follower.ap_id
1037
1038 assert activity.data["object"] == followed.ap_id
1039 end
1040
1041 test "creates an undo activity for the last follow" do
1042 follower = insert(:user)
1043 followed = insert(:user)
1044
1045 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed)
1046 {:ok, activity} = ActivityPub.unfollow(follower, followed)
1047
1048 assert activity.data["type"] == "Undo"
1049 assert activity.data["actor"] == follower.ap_id
1050
1051 embedded_object = activity.data["object"]
1052 assert is_map(embedded_object)
1053 assert embedded_object["type"] == "Follow"
1054 assert embedded_object["object"] == followed.ap_id
1055 assert embedded_object["id"] == follow_activity.data["id"]
1056 end
1057
1058 test "creates an undo activity for a pending follow request" do
1059 follower = insert(:user)
1060 followed = insert(:user, %{locked: true})
1061
1062 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed)
1063 {:ok, activity} = ActivityPub.unfollow(follower, followed)
1064
1065 assert activity.data["type"] == "Undo"
1066 assert activity.data["actor"] == follower.ap_id
1067
1068 embedded_object = activity.data["object"]
1069 assert is_map(embedded_object)
1070 assert embedded_object["type"] == "Follow"
1071 assert embedded_object["object"] == followed.ap_id
1072 assert embedded_object["id"] == follow_activity.data["id"]
1073 end
1074 end
1075
1076 describe "timeline post-processing" do
1077 test "it filters broken threads" do
1078 user1 = insert(:user)
1079 user2 = insert(:user)
1080 user3 = insert(:user)
1081
1082 {:ok, user1} = User.follow(user1, user3)
1083 assert User.following?(user1, user3)
1084
1085 {:ok, user2} = User.follow(user2, user3)
1086 assert User.following?(user2, user3)
1087
1088 {:ok, user3} = User.follow(user3, user2)
1089 assert User.following?(user3, user2)
1090
1091 {:ok, public_activity} = CommonAPI.post(user3, %{status: "hi 1"})
1092
1093 {:ok, private_activity_1} = CommonAPI.post(user3, %{status: "hi 2", visibility: "private"})
1094
1095 {:ok, private_activity_2} =
1096 CommonAPI.post(user2, %{
1097 status: "hi 3",
1098 visibility: "private",
1099 in_reply_to_status_id: private_activity_1.id
1100 })
1101
1102 {:ok, private_activity_3} =
1103 CommonAPI.post(user3, %{
1104 status: "hi 4",
1105 visibility: "private",
1106 in_reply_to_status_id: private_activity_2.id
1107 })
1108
1109 activities =
1110 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)])
1111 |> Enum.map(fn a -> a.id end)
1112
1113 private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"])
1114
1115 assert [public_activity.id, private_activity_1.id, private_activity_3.id] == activities
1116
1117 assert length(activities) == 3
1118
1119 activities =
1120 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)], %{user: user1})
1121 |> Enum.map(fn a -> a.id end)
1122
1123 assert [public_activity.id, private_activity_1.id] == activities
1124 assert length(activities) == 2
1125 end
1126 end
1127
1128 describe "flag/1" do
1129 setup do
1130 reporter = insert(:user)
1131 target_account = insert(:user)
1132 content = "foobar"
1133 {:ok, activity} = CommonAPI.post(target_account, %{status: content})
1134 context = Utils.generate_context_id()
1135
1136 reporter_ap_id = reporter.ap_id
1137 target_ap_id = target_account.ap_id
1138 activity_ap_id = activity.data["id"]
1139
1140 activity_with_object = Activity.get_by_ap_id_with_object(activity_ap_id)
1141
1142 {:ok,
1143 %{
1144 reporter: reporter,
1145 context: context,
1146 target_account: target_account,
1147 reported_activity: activity,
1148 content: content,
1149 activity_ap_id: activity_ap_id,
1150 activity_with_object: activity_with_object,
1151 reporter_ap_id: reporter_ap_id,
1152 target_ap_id: target_ap_id
1153 }}
1154 end
1155
1156 test "it can create a Flag activity",
1157 %{
1158 reporter: reporter,
1159 context: context,
1160 target_account: target_account,
1161 reported_activity: reported_activity,
1162 content: content,
1163 activity_ap_id: activity_ap_id,
1164 activity_with_object: activity_with_object,
1165 reporter_ap_id: reporter_ap_id,
1166 target_ap_id: target_ap_id
1167 } do
1168 assert {:ok, activity} =
1169 ActivityPub.flag(%{
1170 actor: reporter,
1171 context: context,
1172 account: target_account,
1173 statuses: [reported_activity],
1174 content: content
1175 })
1176
1177 note_obj = %{
1178 "type" => "Note",
1179 "id" => activity_ap_id,
1180 "content" => content,
1181 "published" => activity_with_object.object.data["published"],
1182 "actor" =>
1183 AccountView.render("show.json", %{user: target_account, skip_visibility_check: true})
1184 }
1185
1186 assert %Activity{
1187 actor: ^reporter_ap_id,
1188 data: %{
1189 "type" => "Flag",
1190 "content" => ^content,
1191 "context" => ^context,
1192 "object" => [^target_ap_id, ^note_obj]
1193 }
1194 } = activity
1195 end
1196
1197 test_with_mock "strips status data from Flag, before federating it",
1198 %{
1199 reporter: reporter,
1200 context: context,
1201 target_account: target_account,
1202 reported_activity: reported_activity,
1203 content: content
1204 },
1205 Utils,
1206 [:passthrough],
1207 [] do
1208 {:ok, activity} =
1209 ActivityPub.flag(%{
1210 actor: reporter,
1211 context: context,
1212 account: target_account,
1213 statuses: [reported_activity],
1214 content: content
1215 })
1216
1217 new_data =
1218 put_in(activity.data, ["object"], [target_account.ap_id, reported_activity.data["id"]])
1219
1220 assert_called(Utils.maybe_federate(%{activity | data: new_data}))
1221 end
1222 end
1223
1224 test "fetch_activities/2 returns activities addressed to a list " do
1225 user = insert(:user)
1226 member = insert(:user)
1227 {:ok, list} = Pleroma.List.create("foo", user)
1228 {:ok, list} = Pleroma.List.follow(list, member)
1229
1230 {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
1231
1232 activity = Repo.preload(activity, :bookmark)
1233 activity = %Activity{activity | thread_muted?: !!activity.thread_muted?}
1234
1235 assert ActivityPub.fetch_activities([], %{user: user}) == [activity]
1236 end
1237
1238 def data_uri do
1239 File.read!("test/fixtures/avatar_data_uri")
1240 end
1241
1242 describe "fetch_activities_bounded" do
1243 test "fetches private posts for followed users" do
1244 user = insert(:user)
1245
1246 {:ok, activity} =
1247 CommonAPI.post(user, %{
1248 status: "thought I looked cute might delete later :3",
1249 visibility: "private"
1250 })
1251
1252 [result] = ActivityPub.fetch_activities_bounded([user.follower_address], [])
1253 assert result.id == activity.id
1254 end
1255
1256 test "fetches only public posts for other users" do
1257 user = insert(:user)
1258 {:ok, activity} = CommonAPI.post(user, %{status: "#cofe", visibility: "public"})
1259
1260 {:ok, _private_activity} =
1261 CommonAPI.post(user, %{
1262 status: "why is tenshi eating a corndog so cute?",
1263 visibility: "private"
1264 })
1265
1266 [result] = ActivityPub.fetch_activities_bounded([], [user.follower_address])
1267 assert result.id == activity.id
1268 end
1269 end
1270
1271 describe "fetch_follow_information_for_user" do
1272 test "syncronizes following/followers counters" do
1273 user =
1274 insert(:user,
1275 local: false,
1276 follower_address: "http://localhost:4001/users/fuser2/followers",
1277 following_address: "http://localhost:4001/users/fuser2/following"
1278 )
1279
1280 {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
1281 assert info.follower_count == 527
1282 assert info.following_count == 267
1283 end
1284
1285 test "detects hidden followers" do
1286 mock(fn env ->
1287 case env.url do
1288 "http://localhost:4001/users/masto_closed/followers?page=1" ->
1289 %Tesla.Env{status: 403, body: ""}
1290
1291 _ ->
1292 apply(HttpRequestMock, :request, [env])
1293 end
1294 end)
1295
1296 user =
1297 insert(:user,
1298 local: false,
1299 follower_address: "http://localhost:4001/users/masto_closed/followers",
1300 following_address: "http://localhost:4001/users/masto_closed/following"
1301 )
1302
1303 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1304 assert follow_info.hide_followers == true
1305 assert follow_info.hide_follows == false
1306 end
1307
1308 test "detects hidden follows" do
1309 mock(fn env ->
1310 case env.url do
1311 "http://localhost:4001/users/masto_closed/following?page=1" ->
1312 %Tesla.Env{status: 403, body: ""}
1313
1314 _ ->
1315 apply(HttpRequestMock, :request, [env])
1316 end
1317 end)
1318
1319 user =
1320 insert(:user,
1321 local: false,
1322 follower_address: "http://localhost:4001/users/masto_closed/followers",
1323 following_address: "http://localhost:4001/users/masto_closed/following"
1324 )
1325
1326 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1327 assert follow_info.hide_followers == false
1328 assert follow_info.hide_follows == true
1329 end
1330
1331 test "detects hidden follows/followers for friendica" do
1332 user =
1333 insert(:user,
1334 local: false,
1335 follower_address: "http://localhost:8080/followers/fuser3",
1336 following_address: "http://localhost:8080/following/fuser3"
1337 )
1338
1339 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1340 assert follow_info.hide_followers == true
1341 assert follow_info.follower_count == 296
1342 assert follow_info.following_count == 32
1343 assert follow_info.hide_follows == true
1344 end
1345
1346 test "doesn't crash when follower and following counters are hidden" do
1347 mock(fn env ->
1348 case env.url do
1349 "http://localhost:4001/users/masto_hidden_counters/following" ->
1350 json(%{
1351 "@context" => "https://www.w3.org/ns/activitystreams",
1352 "id" => "http://localhost:4001/users/masto_hidden_counters/followers"
1353 })
1354
1355 "http://localhost:4001/users/masto_hidden_counters/following?page=1" ->
1356 %Tesla.Env{status: 403, body: ""}
1357
1358 "http://localhost:4001/users/masto_hidden_counters/followers" ->
1359 json(%{
1360 "@context" => "https://www.w3.org/ns/activitystreams",
1361 "id" => "http://localhost:4001/users/masto_hidden_counters/following"
1362 })
1363
1364 "http://localhost:4001/users/masto_hidden_counters/followers?page=1" ->
1365 %Tesla.Env{status: 403, body: ""}
1366 end
1367 end)
1368
1369 user =
1370 insert(:user,
1371 local: false,
1372 follower_address: "http://localhost:4001/users/masto_hidden_counters/followers",
1373 following_address: "http://localhost:4001/users/masto_hidden_counters/following"
1374 )
1375
1376 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1377
1378 assert follow_info.hide_followers == true
1379 assert follow_info.follower_count == 0
1380 assert follow_info.hide_follows == true
1381 assert follow_info.following_count == 0
1382 end
1383 end
1384
1385 describe "fetch_favourites/3" do
1386 test "returns a favourite activities sorted by adds to favorite" do
1387 user = insert(:user)
1388 other_user = insert(:user)
1389 user1 = insert(:user)
1390 user2 = insert(:user)
1391 {:ok, a1} = CommonAPI.post(user1, %{status: "bla"})
1392 {:ok, _a2} = CommonAPI.post(user2, %{status: "traps are happy"})
1393 {:ok, a3} = CommonAPI.post(user2, %{status: "Trees Are "})
1394 {:ok, a4} = CommonAPI.post(user2, %{status: "Agent Smith "})
1395 {:ok, a5} = CommonAPI.post(user1, %{status: "Red or Blue "})
1396
1397 {:ok, _} = CommonAPI.favorite(user, a4.id)
1398 {:ok, _} = CommonAPI.favorite(other_user, a3.id)
1399 {:ok, _} = CommonAPI.favorite(user, a3.id)
1400 {:ok, _} = CommonAPI.favorite(other_user, a5.id)
1401 {:ok, _} = CommonAPI.favorite(user, a5.id)
1402 {:ok, _} = CommonAPI.favorite(other_user, a4.id)
1403 {:ok, _} = CommonAPI.favorite(user, a1.id)
1404 {:ok, _} = CommonAPI.favorite(other_user, a1.id)
1405 result = ActivityPub.fetch_favourites(user)
1406
1407 assert Enum.map(result, & &1.id) == [a1.id, a5.id, a3.id, a4.id]
1408
1409 result = ActivityPub.fetch_favourites(user, %{limit: 2})
1410 assert Enum.map(result, & &1.id) == [a1.id, a5.id]
1411 end
1412 end
1413
1414 describe "Move activity" do
1415 test "create" do
1416 %{ap_id: old_ap_id} = old_user = insert(:user)
1417 %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
1418 follower = insert(:user)
1419 follower_move_opted_out = insert(:user, allow_following_move: false)
1420
1421 User.follow(follower, old_user)
1422 User.follow(follower_move_opted_out, old_user)
1423
1424 assert User.following?(follower, old_user)
1425 assert User.following?(follower_move_opted_out, old_user)
1426
1427 assert {:ok, activity} = ActivityPub.move(old_user, new_user)
1428
1429 assert %Activity{
1430 actor: ^old_ap_id,
1431 data: %{
1432 "actor" => ^old_ap_id,
1433 "object" => ^old_ap_id,
1434 "target" => ^new_ap_id,
1435 "type" => "Move"
1436 },
1437 local: true
1438 } = activity
1439
1440 params = %{
1441 "op" => "move_following",
1442 "origin_id" => old_user.id,
1443 "target_id" => new_user.id
1444 }
1445
1446 assert_enqueued(worker: Pleroma.Workers.BackgroundWorker, args: params)
1447
1448 Pleroma.Workers.BackgroundWorker.perform(%Oban.Job{args: params})
1449
1450 refute User.following?(follower, old_user)
1451 assert User.following?(follower, new_user)
1452
1453 assert User.following?(follower_move_opted_out, old_user)
1454 refute User.following?(follower_move_opted_out, new_user)
1455
1456 activity = %Activity{activity | object: nil}
1457
1458 assert [%Notification{activity: ^activity}] = Notification.for_user(follower)
1459
1460 assert [%Notification{activity: ^activity}] = Notification.for_user(follower_move_opted_out)
1461 end
1462
1463 test "old user must be in the new user's `also_known_as` list" do
1464 old_user = insert(:user)
1465 new_user = insert(:user)
1466
1467 assert {:error, "Target account must have the origin in `alsoKnownAs`"} =
1468 ActivityPub.move(old_user, new_user)
1469 end
1470 end
1471
1472 test "doesn't retrieve replies activities with exclude_replies" do
1473 user = insert(:user)
1474
1475 {:ok, activity} = CommonAPI.post(user, %{status: "yeah"})
1476
1477 {:ok, _reply} = CommonAPI.post(user, %{status: "yeah", in_reply_to_status_id: activity.id})
1478
1479 [result] = ActivityPub.fetch_public_activities(%{exclude_replies: true})
1480
1481 assert result.id == activity.id
1482
1483 assert length(ActivityPub.fetch_public_activities()) == 2
1484 end
1485
1486 describe "replies filtering with public messages" do
1487 setup :public_messages
1488
1489 test "public timeline", %{users: %{u1: user}} do
1490 activities_ids =
1491 %{}
1492 |> Map.put(:type, ["Create", "Announce"])
1493 |> Map.put(:local_only, false)
1494 |> Map.put(:blocking_user, user)
1495 |> Map.put(:muting_user, user)
1496 |> Map.put(:reply_filtering_user, user)
1497 |> ActivityPub.fetch_public_activities()
1498 |> Enum.map(& &1.id)
1499
1500 assert length(activities_ids) == 16
1501 end
1502
1503 test "public timeline with reply_visibility `following`", %{
1504 users: %{u1: user},
1505 u1: u1,
1506 u2: u2,
1507 u3: u3,
1508 u4: u4,
1509 activities: activities
1510 } do
1511 activities_ids =
1512 %{}
1513 |> Map.put(:type, ["Create", "Announce"])
1514 |> Map.put(:local_only, false)
1515 |> Map.put(:blocking_user, user)
1516 |> Map.put(:muting_user, user)
1517 |> Map.put(:reply_visibility, "following")
1518 |> Map.put(:reply_filtering_user, user)
1519 |> ActivityPub.fetch_public_activities()
1520 |> Enum.map(& &1.id)
1521
1522 assert length(activities_ids) == 14
1523
1524 visible_ids =
1525 Map.values(u1) ++ Map.values(u2) ++ Map.values(u4) ++ Map.values(activities) ++ [u3[:r1]]
1526
1527 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1528 end
1529
1530 test "public timeline with reply_visibility `self`", %{
1531 users: %{u1: user},
1532 u1: u1,
1533 u2: u2,
1534 u3: u3,
1535 u4: u4,
1536 activities: activities
1537 } do
1538 activities_ids =
1539 %{}
1540 |> Map.put(:type, ["Create", "Announce"])
1541 |> Map.put(:local_only, false)
1542 |> Map.put(:blocking_user, user)
1543 |> Map.put(:muting_user, user)
1544 |> Map.put(:reply_visibility, "self")
1545 |> Map.put(:reply_filtering_user, user)
1546 |> ActivityPub.fetch_public_activities()
1547 |> Enum.map(& &1.id)
1548
1549 assert length(activities_ids) == 10
1550 visible_ids = Map.values(u1) ++ [u2[:r1], u3[:r1], u4[:r1]] ++ Map.values(activities)
1551 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1552 end
1553
1554 test "home timeline", %{
1555 users: %{u1: user},
1556 activities: activities,
1557 u1: u1,
1558 u2: u2,
1559 u3: u3,
1560 u4: u4
1561 } do
1562 params =
1563 %{}
1564 |> Map.put(:type, ["Create", "Announce"])
1565 |> Map.put(:blocking_user, user)
1566 |> Map.put(:muting_user, user)
1567 |> Map.put(:user, user)
1568 |> Map.put(:reply_filtering_user, user)
1569
1570 activities_ids =
1571 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1572 |> Enum.map(& &1.id)
1573
1574 assert length(activities_ids) == 13
1575
1576 visible_ids =
1577 Map.values(u1) ++
1578 Map.values(u3) ++
1579 [
1580 activities[:a1],
1581 activities[:a2],
1582 activities[:a4],
1583 u2[:r1],
1584 u2[:r3],
1585 u4[:r1],
1586 u4[:r2]
1587 ]
1588
1589 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1590 end
1591
1592 test "home timeline with reply_visibility `following`", %{
1593 users: %{u1: user},
1594 activities: activities,
1595 u1: u1,
1596 u2: u2,
1597 u3: u3,
1598 u4: u4
1599 } do
1600 params =
1601 %{}
1602 |> Map.put(:type, ["Create", "Announce"])
1603 |> Map.put(:blocking_user, user)
1604 |> Map.put(:muting_user, user)
1605 |> Map.put(:user, user)
1606 |> Map.put(:reply_visibility, "following")
1607 |> Map.put(:reply_filtering_user, user)
1608
1609 activities_ids =
1610 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1611 |> Enum.map(& &1.id)
1612
1613 assert length(activities_ids) == 11
1614
1615 visible_ids =
1616 Map.values(u1) ++
1617 [
1618 activities[:a1],
1619 activities[:a2],
1620 activities[:a4],
1621 u2[:r1],
1622 u2[:r3],
1623 u3[:r1],
1624 u4[:r1],
1625 u4[:r2]
1626 ]
1627
1628 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1629 end
1630
1631 test "home timeline with reply_visibility `self`", %{
1632 users: %{u1: user},
1633 activities: activities,
1634 u1: u1,
1635 u2: u2,
1636 u3: u3,
1637 u4: u4
1638 } do
1639 params =
1640 %{}
1641 |> Map.put(:type, ["Create", "Announce"])
1642 |> Map.put(:blocking_user, user)
1643 |> Map.put(:muting_user, user)
1644 |> Map.put(:user, user)
1645 |> Map.put(:reply_visibility, "self")
1646 |> Map.put(:reply_filtering_user, user)
1647
1648 activities_ids =
1649 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1650 |> Enum.map(& &1.id)
1651
1652 assert length(activities_ids) == 9
1653
1654 visible_ids =
1655 Map.values(u1) ++
1656 [
1657 activities[:a1],
1658 activities[:a2],
1659 activities[:a4],
1660 u2[:r1],
1661 u3[:r1],
1662 u4[:r1]
1663 ]
1664
1665 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1666 end
1667
1668 test "filtering out announces where the user is the actor of the announced message" do
1669 user = insert(:user)
1670 other_user = insert(:user)
1671 third_user = insert(:user)
1672 User.follow(user, other_user)
1673
1674 {:ok, post} = CommonAPI.post(user, %{status: "yo"})
1675 {:ok, other_post} = CommonAPI.post(third_user, %{status: "yo"})
1676 {:ok, _announce} = CommonAPI.repeat(post.id, other_user)
1677 {:ok, _announce} = CommonAPI.repeat(post.id, third_user)
1678 {:ok, announce} = CommonAPI.repeat(other_post.id, other_user)
1679
1680 params = %{
1681 type: ["Announce"]
1682 }
1683
1684 results =
1685 [user.ap_id | User.following(user)]
1686 |> ActivityPub.fetch_activities(params)
1687
1688 assert length(results) == 3
1689
1690 params = %{
1691 type: ["Announce"],
1692 announce_filtering_user: user
1693 }
1694
1695 [result] =
1696 [user.ap_id | User.following(user)]
1697 |> ActivityPub.fetch_activities(params)
1698
1699 assert result.id == announce.id
1700 end
1701 end
1702
1703 describe "replies filtering with private messages" do
1704 setup :private_messages
1705
1706 test "public timeline", %{users: %{u1: user}} do
1707 activities_ids =
1708 %{}
1709 |> Map.put(:type, ["Create", "Announce"])
1710 |> Map.put(:local_only, false)
1711 |> Map.put(:blocking_user, user)
1712 |> Map.put(:muting_user, user)
1713 |> Map.put(:user, user)
1714 |> ActivityPub.fetch_public_activities()
1715 |> Enum.map(& &1.id)
1716
1717 assert activities_ids == []
1718 end
1719
1720 test "public timeline with default reply_visibility `following`", %{users: %{u1: user}} do
1721 activities_ids =
1722 %{}
1723 |> Map.put(:type, ["Create", "Announce"])
1724 |> Map.put(:local_only, false)
1725 |> Map.put(:blocking_user, user)
1726 |> Map.put(:muting_user, user)
1727 |> Map.put(:reply_visibility, "following")
1728 |> Map.put(:reply_filtering_user, user)
1729 |> Map.put(:user, user)
1730 |> ActivityPub.fetch_public_activities()
1731 |> Enum.map(& &1.id)
1732
1733 assert activities_ids == []
1734 end
1735
1736 test "public timeline with default reply_visibility `self`", %{users: %{u1: user}} do
1737 activities_ids =
1738 %{}
1739 |> Map.put(:type, ["Create", "Announce"])
1740 |> Map.put(:local_only, false)
1741 |> Map.put(:blocking_user, user)
1742 |> Map.put(:muting_user, user)
1743 |> Map.put(:reply_visibility, "self")
1744 |> Map.put(:reply_filtering_user, user)
1745 |> Map.put(:user, user)
1746 |> ActivityPub.fetch_public_activities()
1747 |> Enum.map(& &1.id)
1748
1749 assert activities_ids == []
1750 end
1751
1752 test "home timeline", %{users: %{u1: user}} do
1753 params =
1754 %{}
1755 |> Map.put(:type, ["Create", "Announce"])
1756 |> Map.put(:blocking_user, user)
1757 |> Map.put(:muting_user, user)
1758 |> Map.put(:user, user)
1759
1760 activities_ids =
1761 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1762 |> Enum.map(& &1.id)
1763
1764 assert length(activities_ids) == 12
1765 end
1766
1767 test "home timeline with default reply_visibility `following`", %{users: %{u1: user}} do
1768 params =
1769 %{}
1770 |> Map.put(:type, ["Create", "Announce"])
1771 |> Map.put(:blocking_user, user)
1772 |> Map.put(:muting_user, user)
1773 |> Map.put(:user, user)
1774 |> Map.put(:reply_visibility, "following")
1775 |> Map.put(:reply_filtering_user, user)
1776
1777 activities_ids =
1778 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1779 |> Enum.map(& &1.id)
1780
1781 assert length(activities_ids) == 12
1782 end
1783
1784 test "home timeline with default reply_visibility `self`", %{
1785 users: %{u1: user},
1786 activities: activities,
1787 u1: u1,
1788 u2: u2,
1789 u3: u3,
1790 u4: u4
1791 } do
1792 params =
1793 %{}
1794 |> Map.put(:type, ["Create", "Announce"])
1795 |> Map.put(:blocking_user, user)
1796 |> Map.put(:muting_user, user)
1797 |> Map.put(:user, user)
1798 |> Map.put(:reply_visibility, "self")
1799 |> Map.put(:reply_filtering_user, user)
1800
1801 activities_ids =
1802 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1803 |> Enum.map(& &1.id)
1804
1805 assert length(activities_ids) == 10
1806
1807 visible_ids =
1808 Map.values(u1) ++ Map.values(u4) ++ [u2[:r1], u3[:r1]] ++ Map.values(activities)
1809
1810 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1811 end
1812 end
1813
1814 defp public_messages(_) do
1815 [u1, u2, u3, u4] = insert_list(4, :user)
1816 {:ok, u1} = User.follow(u1, u2)
1817 {:ok, u2} = User.follow(u2, u1)
1818 {:ok, u1} = User.follow(u1, u4)
1819 {:ok, u4} = User.follow(u4, u1)
1820
1821 {:ok, u2} = User.follow(u2, u3)
1822 {:ok, u3} = User.follow(u3, u2)
1823
1824 {:ok, a1} = CommonAPI.post(u1, %{status: "Status"})
1825
1826 {:ok, r1_1} =
1827 CommonAPI.post(u2, %{
1828 status: "@#{u1.nickname} reply from u2 to u1",
1829 in_reply_to_status_id: a1.id
1830 })
1831
1832 {:ok, r1_2} =
1833 CommonAPI.post(u3, %{
1834 status: "@#{u1.nickname} reply from u3 to u1",
1835 in_reply_to_status_id: a1.id
1836 })
1837
1838 {:ok, r1_3} =
1839 CommonAPI.post(u4, %{
1840 status: "@#{u1.nickname} reply from u4 to u1",
1841 in_reply_to_status_id: a1.id
1842 })
1843
1844 {:ok, a2} = CommonAPI.post(u2, %{status: "Status"})
1845
1846 {:ok, r2_1} =
1847 CommonAPI.post(u1, %{
1848 status: "@#{u2.nickname} reply from u1 to u2",
1849 in_reply_to_status_id: a2.id
1850 })
1851
1852 {:ok, r2_2} =
1853 CommonAPI.post(u3, %{
1854 status: "@#{u2.nickname} reply from u3 to u2",
1855 in_reply_to_status_id: a2.id
1856 })
1857
1858 {:ok, r2_3} =
1859 CommonAPI.post(u4, %{
1860 status: "@#{u2.nickname} reply from u4 to u2",
1861 in_reply_to_status_id: a2.id
1862 })
1863
1864 {:ok, a3} = CommonAPI.post(u3, %{status: "Status"})
1865
1866 {:ok, r3_1} =
1867 CommonAPI.post(u1, %{
1868 status: "@#{u3.nickname} reply from u1 to u3",
1869 in_reply_to_status_id: a3.id
1870 })
1871
1872 {:ok, r3_2} =
1873 CommonAPI.post(u2, %{
1874 status: "@#{u3.nickname} reply from u2 to u3",
1875 in_reply_to_status_id: a3.id
1876 })
1877
1878 {:ok, r3_3} =
1879 CommonAPI.post(u4, %{
1880 status: "@#{u3.nickname} reply from u4 to u3",
1881 in_reply_to_status_id: a3.id
1882 })
1883
1884 {:ok, a4} = CommonAPI.post(u4, %{status: "Status"})
1885
1886 {:ok, r4_1} =
1887 CommonAPI.post(u1, %{
1888 status: "@#{u4.nickname} reply from u1 to u4",
1889 in_reply_to_status_id: a4.id
1890 })
1891
1892 {:ok, r4_2} =
1893 CommonAPI.post(u2, %{
1894 status: "@#{u4.nickname} reply from u2 to u4",
1895 in_reply_to_status_id: a4.id
1896 })
1897
1898 {:ok, r4_3} =
1899 CommonAPI.post(u3, %{
1900 status: "@#{u4.nickname} reply from u3 to u4",
1901 in_reply_to_status_id: a4.id
1902 })
1903
1904 {:ok,
1905 users: %{u1: u1, u2: u2, u3: u3, u4: u4},
1906 activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id},
1907 u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id},
1908 u2: %{r1: r2_1.id, r2: r2_2.id, r3: r2_3.id},
1909 u3: %{r1: r3_1.id, r2: r3_2.id, r3: r3_3.id},
1910 u4: %{r1: r4_1.id, r2: r4_2.id, r3: r4_3.id}}
1911 end
1912
1913 defp private_messages(_) do
1914 [u1, u2, u3, u4] = insert_list(4, :user)
1915 {:ok, u1} = User.follow(u1, u2)
1916 {:ok, u2} = User.follow(u2, u1)
1917 {:ok, u1} = User.follow(u1, u3)
1918 {:ok, u3} = User.follow(u3, u1)
1919 {:ok, u1} = User.follow(u1, u4)
1920 {:ok, u4} = User.follow(u4, u1)
1921
1922 {:ok, u2} = User.follow(u2, u3)
1923 {:ok, u3} = User.follow(u3, u2)
1924
1925 {:ok, a1} = CommonAPI.post(u1, %{status: "Status", visibility: "private"})
1926
1927 {:ok, r1_1} =
1928 CommonAPI.post(u2, %{
1929 status: "@#{u1.nickname} reply from u2 to u1",
1930 in_reply_to_status_id: a1.id,
1931 visibility: "private"
1932 })
1933
1934 {:ok, r1_2} =
1935 CommonAPI.post(u3, %{
1936 status: "@#{u1.nickname} reply from u3 to u1",
1937 in_reply_to_status_id: a1.id,
1938 visibility: "private"
1939 })
1940
1941 {:ok, r1_3} =
1942 CommonAPI.post(u4, %{
1943 status: "@#{u1.nickname} reply from u4 to u1",
1944 in_reply_to_status_id: a1.id,
1945 visibility: "private"
1946 })
1947
1948 {:ok, a2} = CommonAPI.post(u2, %{status: "Status", visibility: "private"})
1949
1950 {:ok, r2_1} =
1951 CommonAPI.post(u1, %{
1952 status: "@#{u2.nickname} reply from u1 to u2",
1953 in_reply_to_status_id: a2.id,
1954 visibility: "private"
1955 })
1956
1957 {:ok, r2_2} =
1958 CommonAPI.post(u3, %{
1959 status: "@#{u2.nickname} reply from u3 to u2",
1960 in_reply_to_status_id: a2.id,
1961 visibility: "private"
1962 })
1963
1964 {:ok, a3} = CommonAPI.post(u3, %{status: "Status", visibility: "private"})
1965
1966 {:ok, r3_1} =
1967 CommonAPI.post(u1, %{
1968 status: "@#{u3.nickname} reply from u1 to u3",
1969 in_reply_to_status_id: a3.id,
1970 visibility: "private"
1971 })
1972
1973 {:ok, r3_2} =
1974 CommonAPI.post(u2, %{
1975 status: "@#{u3.nickname} reply from u2 to u3",
1976 in_reply_to_status_id: a3.id,
1977 visibility: "private"
1978 })
1979
1980 {:ok, a4} = CommonAPI.post(u4, %{status: "Status", visibility: "private"})
1981
1982 {:ok, r4_1} =
1983 CommonAPI.post(u1, %{
1984 status: "@#{u4.nickname} reply from u1 to u4",
1985 in_reply_to_status_id: a4.id,
1986 visibility: "private"
1987 })
1988
1989 {:ok,
1990 users: %{u1: u1, u2: u2, u3: u3, u4: u4},
1991 activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id},
1992 u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id},
1993 u2: %{r1: r2_1.id, r2: r2_2.id},
1994 u3: %{r1: r3_1.id, r2: r3_2.id},
1995 u4: %{r1: r4_1.id}}
1996 end
1997
1998 describe "maybe_update_follow_information/1" do
1999 setup do
2000 clear_config([:instance, :external_user_synchronization], true)
2001
2002 user = %{
2003 local: false,
2004 ap_id: "https://gensokyo.2hu/users/raymoo",
2005 following_address: "https://gensokyo.2hu/users/following",
2006 follower_address: "https://gensokyo.2hu/users/followers",
2007 type: "Person"
2008 }
2009
2010 %{user: user}
2011 end
2012
2013 test "logs an error when it can't fetch the info", %{user: user} do
2014 assert capture_log(fn ->
2015 ActivityPub.maybe_update_follow_information(user)
2016 end) =~ "Follower/Following counter update for #{user.ap_id} failed"
2017 end
2018
2019 test "just returns the input if the user type is Application", %{
2020 user: user
2021 } do
2022 user =
2023 user
2024 |> Map.put(:type, "Application")
2025
2026 refute capture_log(fn ->
2027 assert ^user = ActivityPub.maybe_update_follow_information(user)
2028 end) =~ "Follower/Following counter update for #{user.ap_id} failed"
2029 end
2030
2031 test "it just returns the input if the user has no following/follower addresses", %{
2032 user: user
2033 } do
2034 user =
2035 user
2036 |> Map.put(:following_address, nil)
2037 |> Map.put(:follower_address, nil)
2038
2039 refute capture_log(fn ->
2040 assert ^user = ActivityPub.maybe_update_follow_information(user)
2041 end) =~ "Follower/Following counter update for #{user.ap_id} failed"
2042 end
2043 end
2044
2045 describe "global activity expiration" do
2046 setup do: clear_config([:mrf, :policies])
2047
2048 test "creates an activity expiration for local Create activities" do
2049 Pleroma.Config.put(
2050 [:mrf, :policies],
2051 Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy
2052 )
2053
2054 {:ok, %{id: id_create}} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"})
2055 {:ok, _follow} = ActivityBuilder.insert(%{"type" => "Follow", "context" => "3hu"})
2056
2057 assert [%{activity_id: ^id_create}] = Pleroma.ActivityExpiration |> Repo.all()
2058 end
2059 end
2060
2061 describe "handling of clashing nicknames" do
2062 test "renames an existing user with a clashing nickname and a different ap id" do
2063 orig_user =
2064 insert(
2065 :user,
2066 local: false,
2067 nickname: "admin@mastodon.example.org",
2068 ap_id: "http://mastodon.example.org/users/harinezumigari"
2069 )
2070
2071 %{
2072 nickname: orig_user.nickname,
2073 ap_id: orig_user.ap_id <> "part_2"
2074 }
2075 |> ActivityPub.maybe_handle_clashing_nickname()
2076
2077 user = User.get_by_id(orig_user.id)
2078
2079 assert user.nickname == "#{orig_user.id}.admin@mastodon.example.org"
2080 end
2081
2082 test "does nothing with a clashing nickname and the same ap id" do
2083 orig_user =
2084 insert(
2085 :user,
2086 local: false,
2087 nickname: "admin@mastodon.example.org",
2088 ap_id: "http://mastodon.example.org/users/harinezumigari"
2089 )
2090
2091 %{
2092 nickname: orig_user.nickname,
2093 ap_id: orig_user.ap_id
2094 }
2095 |> ActivityPub.maybe_handle_clashing_nickname()
2096
2097 user = User.get_by_id(orig_user.id)
2098
2099 assert user.nickname == orig_user.nickname
2100 end
2101 end
2102 end