44763daf70bcf544c273dea4866693b3444b6495
[akkoma] / test / pleroma / user_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.UserTest do
6 alias Pleroma.Activity
7 alias Pleroma.Builders.UserBuilder
8 alias Pleroma.Notification
9 alias Pleroma.Object
10 alias Pleroma.Repo
11 alias Pleroma.Tests.ObanHelpers
12 alias Pleroma.User
13 alias Pleroma.Web.ActivityPub.ActivityPub
14 alias Pleroma.Web.CommonAPI
15
16 use Pleroma.DataCase
17 use Oban.Testing, repo: Pleroma.Repo
18
19 import Pleroma.Factory
20 import ExUnit.CaptureLog
21 import Swoosh.TestAssertions
22
23 setup_all do
24 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
25 :ok
26 end
27
28 setup do: clear_config([:instance, :account_activation_required])
29
30 describe "service actors" do
31 test "returns updated invisible actor" do
32 uri = "#{Pleroma.Web.Endpoint.url()}/relay"
33 followers_uri = "#{uri}/followers"
34
35 insert(
36 :user,
37 %{
38 nickname: "relay",
39 invisible: false,
40 local: true,
41 ap_id: uri,
42 follower_address: followers_uri
43 }
44 )
45
46 actor = User.get_or_create_service_actor_by_ap_id(uri, "relay")
47 assert actor.invisible
48 end
49
50 test "returns relay user" do
51 uri = "#{Pleroma.Web.Endpoint.url()}/relay"
52 followers_uri = "#{uri}/followers"
53
54 assert %User{
55 nickname: "relay",
56 invisible: true,
57 local: true,
58 ap_id: ^uri,
59 follower_address: ^followers_uri
60 } = User.get_or_create_service_actor_by_ap_id(uri, "relay")
61
62 assert capture_log(fn ->
63 refute User.get_or_create_service_actor_by_ap_id("/relay", "relay")
64 end) =~ "Cannot create service actor:"
65 end
66
67 test "returns invisible actor" do
68 uri = "#{Pleroma.Web.Endpoint.url()}/internal/fetch-test"
69 followers_uri = "#{uri}/followers"
70 user = User.get_or_create_service_actor_by_ap_id(uri, "internal.fetch-test")
71
72 assert %User{
73 nickname: "internal.fetch-test",
74 invisible: true,
75 local: true,
76 ap_id: ^uri,
77 follower_address: ^followers_uri
78 } = user
79
80 user2 = User.get_or_create_service_actor_by_ap_id(uri, "internal.fetch-test")
81 assert user.id == user2.id
82 end
83 end
84
85 describe "AP ID user relationships" do
86 setup do
87 {:ok, user: insert(:user)}
88 end
89
90 test "outgoing_relationships_ap_ids/1", %{user: user} do
91 rel_types = [:block, :mute, :notification_mute, :reblog_mute, :inverse_subscription]
92
93 ap_ids_by_rel =
94 Enum.into(
95 rel_types,
96 %{},
97 fn rel_type ->
98 rel_records =
99 insert_list(2, :user_relationship, %{source: user, relationship_type: rel_type})
100
101 ap_ids = Enum.map(rel_records, fn rr -> Repo.preload(rr, :target).target.ap_id end)
102 {rel_type, Enum.sort(ap_ids)}
103 end
104 )
105
106 assert ap_ids_by_rel[:block] == Enum.sort(User.blocked_users_ap_ids(user))
107 assert ap_ids_by_rel[:block] == Enum.sort(Enum.map(User.blocked_users(user), & &1.ap_id))
108
109 assert ap_ids_by_rel[:mute] == Enum.sort(User.muted_users_ap_ids(user))
110 assert ap_ids_by_rel[:mute] == Enum.sort(Enum.map(User.muted_users(user), & &1.ap_id))
111
112 assert ap_ids_by_rel[:notification_mute] ==
113 Enum.sort(User.notification_muted_users_ap_ids(user))
114
115 assert ap_ids_by_rel[:notification_mute] ==
116 Enum.sort(Enum.map(User.notification_muted_users(user), & &1.ap_id))
117
118 assert ap_ids_by_rel[:reblog_mute] == Enum.sort(User.reblog_muted_users_ap_ids(user))
119
120 assert ap_ids_by_rel[:reblog_mute] ==
121 Enum.sort(Enum.map(User.reblog_muted_users(user), & &1.ap_id))
122
123 assert ap_ids_by_rel[:inverse_subscription] == Enum.sort(User.subscriber_users_ap_ids(user))
124
125 assert ap_ids_by_rel[:inverse_subscription] ==
126 Enum.sort(Enum.map(User.subscriber_users(user), & &1.ap_id))
127
128 outgoing_relationships_ap_ids = User.outgoing_relationships_ap_ids(user, rel_types)
129
130 assert ap_ids_by_rel ==
131 Enum.into(outgoing_relationships_ap_ids, %{}, fn {k, v} -> {k, Enum.sort(v)} end)
132 end
133 end
134
135 describe "when tags are nil" do
136 test "tagging a user" do
137 user = insert(:user, %{tags: nil})
138 user = User.tag(user, ["cool", "dude"])
139
140 assert "cool" in user.tags
141 assert "dude" in user.tags
142 end
143
144 test "untagging a user" do
145 user = insert(:user, %{tags: nil})
146 user = User.untag(user, ["cool", "dude"])
147
148 assert user.tags == []
149 end
150 end
151
152 test "ap_id returns the activity pub id for the user" do
153 user = UserBuilder.build()
154
155 expected_ap_id = "#{Pleroma.Web.Endpoint.url()}/users/#{user.nickname}"
156
157 assert expected_ap_id == User.ap_id(user)
158 end
159
160 test "ap_followers returns the followers collection for the user" do
161 user = UserBuilder.build()
162
163 expected_followers_collection = "#{User.ap_id(user)}/followers"
164
165 assert expected_followers_collection == User.ap_followers(user)
166 end
167
168 test "ap_following returns the following collection for the user" do
169 user = UserBuilder.build()
170
171 expected_followers_collection = "#{User.ap_id(user)}/following"
172
173 assert expected_followers_collection == User.ap_following(user)
174 end
175
176 test "returns all pending follow requests" do
177 unlocked = insert(:user)
178 locked = insert(:user, is_locked: true)
179 follower = insert(:user)
180
181 CommonAPI.follow(follower, unlocked)
182 CommonAPI.follow(follower, locked)
183
184 assert [] = User.get_follow_requests(unlocked)
185 assert [activity] = User.get_follow_requests(locked)
186
187 assert activity
188 end
189
190 test "doesn't return already accepted or duplicate follow requests" do
191 locked = insert(:user, is_locked: true)
192 pending_follower = insert(:user)
193 accepted_follower = insert(:user)
194
195 CommonAPI.follow(pending_follower, locked)
196 CommonAPI.follow(pending_follower, locked)
197 CommonAPI.follow(accepted_follower, locked)
198
199 Pleroma.FollowingRelationship.update(accepted_follower, locked, :follow_accept)
200
201 assert [^pending_follower] = User.get_follow_requests(locked)
202 end
203
204 test "doesn't return follow requests for deactivated accounts" do
205 locked = insert(:user, is_locked: true)
206 pending_follower = insert(:user, %{is_active: false})
207
208 CommonAPI.follow(pending_follower, locked)
209
210 refute pending_follower.is_active
211 assert [] = User.get_follow_requests(locked)
212 end
213
214 test "clears follow requests when requester is blocked" do
215 followed = insert(:user, is_locked: true)
216 follower = insert(:user)
217
218 CommonAPI.follow(follower, followed)
219 assert [_activity] = User.get_follow_requests(followed)
220
221 {:ok, _user_relationship} = User.block(followed, follower)
222 assert [] = User.get_follow_requests(followed)
223 end
224
225 test "follow_all follows mutliple users" do
226 user = insert(:user)
227 followed_zero = insert(:user)
228 followed_one = insert(:user)
229 followed_two = insert(:user)
230 blocked = insert(:user)
231 not_followed = insert(:user)
232 reverse_blocked = insert(:user)
233
234 {:ok, _user_relationship} = User.block(user, blocked)
235 {:ok, _user_relationship} = User.block(reverse_blocked, user)
236
237 {:ok, user, followed_zero} = User.follow(user, followed_zero)
238
239 {:ok, user} = User.follow_all(user, [followed_one, followed_two, blocked, reverse_blocked])
240
241 assert User.following?(user, followed_one)
242 assert User.following?(user, followed_two)
243 assert User.following?(user, followed_zero)
244 refute User.following?(user, not_followed)
245 refute User.following?(user, blocked)
246 refute User.following?(user, reverse_blocked)
247 end
248
249 test "follow_all follows mutliple users without duplicating" do
250 user = insert(:user)
251 followed_zero = insert(:user)
252 followed_one = insert(:user)
253 followed_two = insert(:user)
254
255 {:ok, user} = User.follow_all(user, [followed_zero, followed_one])
256 assert length(User.following(user)) == 3
257
258 {:ok, user} = User.follow_all(user, [followed_one, followed_two])
259 assert length(User.following(user)) == 4
260 end
261
262 test "follow takes a user and another user" do
263 user = insert(:user)
264 followed = insert(:user)
265
266 {:ok, user, followed} = User.follow(user, followed)
267
268 user = User.get_cached_by_id(user.id)
269 followed = User.get_cached_by_ap_id(followed.ap_id)
270
271 assert followed.follower_count == 1
272 assert user.following_count == 1
273
274 assert User.ap_followers(followed) in User.following(user)
275 end
276
277 test "can't follow a deactivated users" do
278 user = insert(:user)
279 followed = insert(:user, %{is_active: false})
280
281 {:error, _} = User.follow(user, followed)
282 end
283
284 test "can't follow a user who blocked us" do
285 blocker = insert(:user)
286 blockee = insert(:user)
287
288 {:ok, _user_relationship} = User.block(blocker, blockee)
289
290 {:error, _} = User.follow(blockee, blocker)
291 end
292
293 test "can't subscribe to a user who blocked us" do
294 blocker = insert(:user)
295 blocked = insert(:user)
296
297 {:ok, _user_relationship} = User.block(blocker, blocked)
298
299 {:error, _} = User.subscribe(blocked, blocker)
300 end
301
302 test "local users do not automatically follow local locked accounts" do
303 follower = insert(:user, is_locked: true)
304 followed = insert(:user, is_locked: true)
305
306 {:ok, follower, followed} = User.maybe_direct_follow(follower, followed)
307
308 refute User.following?(follower, followed)
309 end
310
311 describe "unfollow/2" do
312 setup do: clear_config([:instance, :external_user_synchronization])
313
314 test "unfollow with synchronizes external user" do
315 clear_config([:instance, :external_user_synchronization], true)
316
317 followed =
318 insert(:user,
319 nickname: "fuser1",
320 follower_address: "http://localhost:4001/users/fuser1/followers",
321 following_address: "http://localhost:4001/users/fuser1/following",
322 ap_id: "http://localhost:4001/users/fuser1"
323 )
324
325 user =
326 insert(:user, %{
327 local: false,
328 nickname: "fuser2",
329 ap_id: "http://localhost:4001/users/fuser2",
330 follower_address: "http://localhost:4001/users/fuser2/followers",
331 following_address: "http://localhost:4001/users/fuser2/following"
332 })
333
334 {:ok, user, followed} = User.follow(user, followed, :follow_accept)
335
336 {:ok, user, _activity} = User.unfollow(user, followed)
337
338 user = User.get_cached_by_id(user.id)
339
340 assert User.following(user) == []
341 end
342
343 test "unfollow takes a user and another user" do
344 followed = insert(:user)
345 user = insert(:user)
346
347 {:ok, user, followed} = User.follow(user, followed, :follow_accept)
348
349 assert User.following(user) == [user.follower_address, followed.follower_address]
350
351 {:ok, user, _activity} = User.unfollow(user, followed)
352
353 assert User.following(user) == [user.follower_address]
354 end
355
356 test "unfollow doesn't unfollow yourself" do
357 user = insert(:user)
358
359 {:error, _} = User.unfollow(user, user)
360
361 assert User.following(user) == [user.follower_address]
362 end
363 end
364
365 test "test if a user is following another user" do
366 followed = insert(:user)
367 user = insert(:user)
368 User.follow(user, followed, :follow_accept)
369
370 assert User.following?(user, followed)
371 refute User.following?(followed, user)
372 end
373
374 test "fetches correct profile for nickname beginning with number" do
375 # Use old-style integer ID to try to reproduce the problem
376 user = insert(:user, %{id: 1080})
377 user_with_numbers = insert(:user, %{nickname: "#{user.id}garbage"})
378 assert user_with_numbers == User.get_cached_by_nickname_or_id(user_with_numbers.nickname)
379 end
380
381 describe "user registration" do
382 @full_user_data %{
383 bio: "A guy",
384 name: "my name",
385 nickname: "nick",
386 password: "test",
387 password_confirmation: "test",
388 email: "email@example.com"
389 }
390
391 setup do: clear_config([:instance, :autofollowed_nicknames])
392 setup do: clear_config([:instance, :autofollowing_nicknames])
393 setup do: clear_config([:welcome])
394 setup do: clear_config([:instance, :account_activation_required])
395
396 test "it autofollows accounts that are set for it" do
397 user = insert(:user)
398 remote_user = insert(:user, %{local: false})
399
400 clear_config([:instance, :autofollowed_nicknames], [
401 user.nickname,
402 remote_user.nickname
403 ])
404
405 cng = User.register_changeset(%User{}, @full_user_data)
406
407 {:ok, registered_user} = User.register(cng)
408
409 assert User.following?(registered_user, user)
410 refute User.following?(registered_user, remote_user)
411 end
412
413 test "it adds automatic followers for new registered accounts" do
414 user1 = insert(:user)
415 user2 = insert(:user)
416
417 clear_config([:instance, :autofollowing_nicknames], [
418 user1.nickname,
419 user2.nickname
420 ])
421
422 cng = User.register_changeset(%User{}, @full_user_data)
423
424 {:ok, registered_user} = User.register(cng)
425
426 assert User.following?(user1, registered_user)
427 assert User.following?(user2, registered_user)
428 end
429
430 test "it sends a welcome message if it is set" do
431 welcome_user = insert(:user)
432 clear_config([:welcome, :direct_message, :enabled], true)
433 clear_config([:welcome, :direct_message, :sender_nickname], welcome_user.nickname)
434 clear_config([:welcome, :direct_message, :message], "Hello, this is a direct message")
435
436 cng = User.register_changeset(%User{}, @full_user_data)
437 {:ok, registered_user} = User.register(cng)
438 ObanHelpers.perform_all()
439
440 activity = Repo.one(Pleroma.Activity)
441 assert registered_user.ap_id in activity.recipients
442 assert Object.normalize(activity, fetch: false).data["content"] =~ "direct message"
443 assert activity.actor == welcome_user.ap_id
444 end
445
446 setup do:
447 clear_config(
448 [:mrf_simple],
449 %{
450 media_removal: [],
451 media_nsfw: [],
452 federated_timeline_removal: [],
453 report_removal: [],
454 reject: [],
455 followers_only: [],
456 accept: [],
457 avatar_removal: [],
458 banner_removal: [],
459 reject_deletes: []
460 }
461 )
462
463 setup do:
464 clear_config(:mrf,
465 policies: [
466 Pleroma.Web.ActivityPub.MRF.SimplePolicy
467 ]
468 )
469
470 test "it sends a welcome email message if it is set" do
471 welcome_user = insert(:user)
472 clear_config([:welcome, :email, :enabled], true)
473 clear_config([:welcome, :email, :sender], welcome_user.email)
474
475 clear_config(
476 [:welcome, :email, :subject],
477 "Hello, welcome to cool site: <%= instance_name %>"
478 )
479
480 instance_name = Pleroma.Config.get([:instance, :name])
481
482 cng = User.register_changeset(%User{}, @full_user_data)
483 {:ok, registered_user} = User.register(cng)
484 ObanHelpers.perform_all()
485
486 assert_email_sent(
487 from: {instance_name, welcome_user.email},
488 to: {registered_user.name, registered_user.email},
489 subject: "Hello, welcome to cool site: #{instance_name}",
490 html_body: "Welcome to #{instance_name}"
491 )
492 end
493
494 test "it sends a confirm email" do
495 clear_config([:instance, :account_activation_required], true)
496
497 cng = User.register_changeset(%User{}, @full_user_data)
498 {:ok, registered_user} = User.register(cng)
499 ObanHelpers.perform_all()
500
501 Pleroma.Emails.UserEmail.account_confirmation_email(registered_user)
502 # temporary hackney fix until hackney max_connections bug is fixed
503 # https://git.pleroma.social/pleroma/pleroma/-/issues/2101
504 |> Swoosh.Email.put_private(:hackney_options, ssl_options: [versions: [:"tlsv1.2"]])
505 |> assert_email_sent()
506 end
507
508 test "sends a pending approval email" do
509 clear_config([:instance, :account_approval_required], true)
510
511 {:ok, user} =
512 User.register_changeset(%User{}, @full_user_data)
513 |> User.register()
514
515 ObanHelpers.perform_all()
516
517 assert_email_sent(
518 from: Pleroma.Config.Helpers.sender(),
519 to: {user.name, user.email},
520 subject: "Your account is awaiting approval"
521 )
522 end
523
524 test "it sends a registration confirmed email if no others will be sent" do
525 clear_config([:welcome, :email, :enabled], false)
526 clear_config([:instance, :account_activation_required], false)
527 clear_config([:instance, :account_approval_required], false)
528
529 {:ok, user} =
530 User.register_changeset(%User{}, @full_user_data)
531 |> User.register()
532
533 ObanHelpers.perform_all()
534
535 instance_name = Pleroma.Config.get([:instance, :name])
536 sender = Pleroma.Config.get([:instance, :notify_email])
537
538 assert_email_sent(
539 from: {instance_name, sender},
540 to: {user.name, user.email},
541 subject: "Account registered on #{instance_name}"
542 )
543 end
544
545 test "it fails gracefully with invalid email config" do
546 cng = User.register_changeset(%User{}, @full_user_data)
547
548 # Disable the mailer but enable all the things that want to send emails
549 clear_config([Pleroma.Emails.Mailer, :enabled], false)
550 clear_config([:instance, :account_activation_required], true)
551 clear_config([:instance, :account_approval_required], true)
552 clear_config([:welcome, :email, :enabled], true)
553 clear_config([:welcome, :email, :sender], "lain@lain.com")
554
555 # The user is still created
556 assert {:ok, %User{nickname: "nick"}} = User.register(cng)
557
558 # No emails are sent
559 ObanHelpers.perform_all()
560 refute_email_sent()
561 end
562
563 test "it requires an email, name, nickname and password, bio is optional when account_activation_required is enabled" do
564 clear_config([:instance, :account_activation_required], true)
565
566 @full_user_data
567 |> Map.keys()
568 |> Enum.each(fn key ->
569 params = Map.delete(@full_user_data, key)
570 changeset = User.register_changeset(%User{}, params)
571
572 assert if key == :bio, do: changeset.valid?, else: not changeset.valid?
573 end)
574 end
575
576 test "it requires an name, nickname and password, bio and email are optional when account_activation_required is disabled" do
577 clear_config([:instance, :account_activation_required], false)
578
579 @full_user_data
580 |> Map.keys()
581 |> Enum.each(fn key ->
582 params = Map.delete(@full_user_data, key)
583 changeset = User.register_changeset(%User{}, params)
584
585 assert if key in [:bio, :email], do: changeset.valid?, else: not changeset.valid?
586 end)
587 end
588
589 test "it restricts certain nicknames" do
590 [restricted_name | _] = Pleroma.Config.get([User, :restricted_nicknames])
591
592 assert is_bitstring(restricted_name)
593
594 params =
595 @full_user_data
596 |> Map.put(:nickname, restricted_name)
597
598 changeset = User.register_changeset(%User{}, params)
599
600 refute changeset.valid?
601 end
602
603 test "it blocks blacklisted email domains" do
604 clear_config([User, :email_blacklist], ["trolling.world"])
605
606 # Block with match
607 params = Map.put(@full_user_data, :email, "troll@trolling.world")
608 changeset = User.register_changeset(%User{}, params)
609 refute changeset.valid?
610
611 # Block with subdomain match
612 params = Map.put(@full_user_data, :email, "troll@gnomes.trolling.world")
613 changeset = User.register_changeset(%User{}, params)
614 refute changeset.valid?
615
616 # Pass with different domains that are similar
617 params = Map.put(@full_user_data, :email, "troll@gnomestrolling.world")
618 changeset = User.register_changeset(%User{}, params)
619 assert changeset.valid?
620
621 params = Map.put(@full_user_data, :email, "troll@trolling.world.us")
622 changeset = User.register_changeset(%User{}, params)
623 assert changeset.valid?
624 end
625
626 test "it sets the password_hash, ap_id, private key and followers collection address" do
627 changeset = User.register_changeset(%User{}, @full_user_data)
628
629 assert changeset.valid?
630
631 assert is_binary(changeset.changes[:password_hash])
632 assert is_binary(changeset.changes[:keys])
633 assert changeset.changes[:ap_id] == User.ap_id(%User{nickname: @full_user_data.nickname})
634 assert is_binary(changeset.changes[:keys])
635 assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers"
636 end
637
638 test "it creates a confirmed user" do
639 changeset = User.register_changeset(%User{}, @full_user_data)
640 assert changeset.valid?
641
642 {:ok, user} = Repo.insert(changeset)
643
644 assert user.is_confirmed
645 end
646 end
647
648 describe "user registration, with :account_activation_required" do
649 @full_user_data %{
650 bio: "A guy",
651 name: "my name",
652 nickname: "nick",
653 password: "test",
654 password_confirmation: "test",
655 email: "email@example.com"
656 }
657 setup do: clear_config([:instance, :account_activation_required], true)
658
659 test "it creates unconfirmed user" do
660 changeset = User.register_changeset(%User{}, @full_user_data)
661 assert changeset.valid?
662
663 {:ok, user} = Repo.insert(changeset)
664
665 refute user.is_confirmed
666 assert user.confirmation_token
667 end
668
669 test "it creates confirmed user if :confirmed option is given" do
670 changeset = User.register_changeset(%User{}, @full_user_data, confirmed: true)
671 assert changeset.valid?
672
673 {:ok, user} = Repo.insert(changeset)
674
675 assert user.is_confirmed
676 refute user.confirmation_token
677 end
678 end
679
680 describe "user registration, with :account_approval_required" do
681 @full_user_data %{
682 bio: "A guy",
683 name: "my name",
684 nickname: "nick",
685 password: "test",
686 password_confirmation: "test",
687 email: "email@example.com",
688 registration_reason: "I'm a cool guy :)"
689 }
690 setup do: clear_config([:instance, :account_approval_required], true)
691
692 test "it creates unapproved user" do
693 changeset = User.register_changeset(%User{}, @full_user_data)
694 assert changeset.valid?
695
696 {:ok, user} = Repo.insert(changeset)
697
698 refute user.is_approved
699 assert user.registration_reason == "I'm a cool guy :)"
700 end
701
702 test "it restricts length of registration reason" do
703 reason_limit = Pleroma.Config.get([:instance, :registration_reason_length])
704
705 assert is_integer(reason_limit)
706
707 params =
708 @full_user_data
709 |> Map.put(
710 :registration_reason,
711 "Quia et nesciunt dolores numquam ipsam nisi sapiente soluta. Ullam repudiandae nisi quam porro officiis officiis ad. Consequatur animi velit ex quia. Odit voluptatem perferendis quia ut nisi. Dignissimos sit soluta atque aliquid dolorem ut dolorum ut. Labore voluptates iste iusto amet voluptatum earum. Ad fugit illum nam eos ut nemo. Pariatur ea fuga non aspernatur. Dignissimos debitis officia corporis est nisi ab et. Atque itaque alias eius voluptas minus. Accusamus numquam tempore occaecati in."
712 )
713
714 changeset = User.register_changeset(%User{}, params)
715
716 refute changeset.valid?
717 end
718 end
719
720 describe "get_or_fetch/1" do
721 test "gets an existing user by nickname" do
722 user = insert(:user)
723 {:ok, fetched_user} = User.get_or_fetch(user.nickname)
724
725 assert user == fetched_user
726 end
727
728 test "gets an existing user by ap_id" do
729 ap_id = "http://mastodon.example.org/users/admin"
730
731 user =
732 insert(
733 :user,
734 local: false,
735 nickname: "admin@mastodon.example.org",
736 ap_id: ap_id
737 )
738
739 {:ok, fetched_user} = User.get_or_fetch(ap_id)
740 freshed_user = refresh_record(user)
741 assert freshed_user == fetched_user
742 end
743
744 test "gets an existing user by nickname starting with http" do
745 user = insert(:user, nickname: "httpssome")
746 {:ok, fetched_user} = User.get_or_fetch("httpssome")
747
748 assert user == fetched_user
749 end
750 end
751
752 describe "get_or_fetch/1 remote users with tld, while BE is runned on subdomain" do
753 setup do: clear_config([Pleroma.Web.WebFinger, :update_nickname_on_user_fetch], true)
754
755 test "for mastodon" do
756 Tesla.Mock.mock(fn
757 %{url: "https://example.com/.well-known/host-meta"} ->
758 %Tesla.Env{
759 status: 302,
760 headers: [{"location", "https://sub.example.com/.well-known/host-meta"}]
761 }
762
763 %{url: "https://sub.example.com/.well-known/host-meta"} ->
764 %Tesla.Env{
765 status: 200,
766 body:
767 "test/fixtures/webfinger/masto-host-meta.xml"
768 |> File.read!()
769 |> String.replace("{{domain}}", "sub.example.com")
770 }
771
772 %{url: "https://sub.example.com/.well-known/webfinger?resource=acct:a@example.com"} ->
773 %Tesla.Env{
774 status: 200,
775 body:
776 "test/fixtures/webfinger/masto-webfinger.json"
777 |> File.read!()
778 |> String.replace("{{nickname}}", "a")
779 |> String.replace("{{domain}}", "example.com")
780 |> String.replace("{{subdomain}}", "sub.example.com"),
781 headers: [{"content-type", "application/jrd+json"}]
782 }
783
784 %{url: "https://sub.example.com/users/a"} ->
785 %Tesla.Env{
786 status: 200,
787 body:
788 "test/fixtures/webfinger/masto-user.json"
789 |> File.read!()
790 |> String.replace("{{nickname}}", "a")
791 |> String.replace("{{domain}}", "sub.example.com"),
792 headers: [{"content-type", "application/activity+json"}]
793 }
794
795 %{url: "https://sub.example.com/users/a/collections/featured"} ->
796 %Tesla.Env{
797 status: 200,
798 body:
799 File.read!("test/fixtures/users_mock/masto_featured.json")
800 |> String.replace("{{domain}}", "sub.example.com")
801 |> String.replace("{{nickname}}", "a"),
802 headers: [{"content-type", "application/activity+json"}]
803 }
804 end)
805
806 ap_id = "a@example.com"
807 {:ok, fetched_user} = User.get_or_fetch(ap_id)
808
809 assert fetched_user.ap_id == "https://sub.example.com/users/a"
810 assert fetched_user.nickname == "a@example.com"
811 end
812
813 test "for pleroma" do
814 Tesla.Mock.mock(fn
815 %{url: "https://example.com/.well-known/host-meta"} ->
816 %Tesla.Env{
817 status: 302,
818 headers: [{"location", "https://sub.example.com/.well-known/host-meta"}]
819 }
820
821 %{url: "https://sub.example.com/.well-known/host-meta"} ->
822 %Tesla.Env{
823 status: 200,
824 body:
825 "test/fixtures/webfinger/pleroma-host-meta.xml"
826 |> File.read!()
827 |> String.replace("{{domain}}", "sub.example.com")
828 }
829
830 %{url: "https://sub.example.com/.well-known/webfinger?resource=acct:a@example.com"} ->
831 %Tesla.Env{
832 status: 200,
833 body:
834 "test/fixtures/webfinger/pleroma-webfinger.json"
835 |> File.read!()
836 |> String.replace("{{nickname}}", "a")
837 |> String.replace("{{domain}}", "example.com")
838 |> String.replace("{{subdomain}}", "sub.example.com"),
839 headers: [{"content-type", "application/jrd+json"}]
840 }
841
842 %{url: "https://sub.example.com/users/a"} ->
843 %Tesla.Env{
844 status: 200,
845 body:
846 "test/fixtures/webfinger/pleroma-user.json"
847 |> File.read!()
848 |> String.replace("{{nickname}}", "a")
849 |> String.replace("{{domain}}", "sub.example.com"),
850 headers: [{"content-type", "application/activity+json"}]
851 }
852 end)
853
854 ap_id = "a@example.com"
855 {:ok, fetched_user} = User.get_or_fetch(ap_id)
856
857 assert fetched_user.ap_id == "https://sub.example.com/users/a"
858 assert fetched_user.nickname == "a@example.com"
859 end
860 end
861
862 describe "fetching a user from nickname or trying to build one" do
863 test "gets an existing user" do
864 user = insert(:user)
865 {:ok, fetched_user} = User.get_or_fetch_by_nickname(user.nickname)
866
867 assert user == fetched_user
868 end
869
870 test "gets an existing user, case insensitive" do
871 user = insert(:user, nickname: "nick")
872 {:ok, fetched_user} = User.get_or_fetch_by_nickname("NICK")
873
874 assert user == fetched_user
875 end
876
877 test "gets an existing user by fully qualified nickname" do
878 user = insert(:user)
879
880 {:ok, fetched_user} =
881 User.get_or_fetch_by_nickname(user.nickname <> "@" <> Pleroma.Web.Endpoint.host())
882
883 assert user == fetched_user
884 end
885
886 test "gets an existing user by fully qualified nickname, case insensitive" do
887 user = insert(:user, nickname: "nick")
888 casing_altered_fqn = String.upcase(user.nickname <> "@" <> Pleroma.Web.Endpoint.host())
889
890 {:ok, fetched_user} = User.get_or_fetch_by_nickname(casing_altered_fqn)
891
892 assert user == fetched_user
893 end
894
895 @tag capture_log: true
896 test "returns nil if no user could be fetched" do
897 {:error, fetched_user} = User.get_or_fetch_by_nickname("nonexistant@social.heldscal.la")
898 assert fetched_user == "not found nonexistant@social.heldscal.la"
899 end
900
901 test "returns nil for nonexistant local user" do
902 {:error, fetched_user} = User.get_or_fetch_by_nickname("nonexistant")
903 assert fetched_user == "not found nonexistant"
904 end
905
906 test "updates an existing user, if stale" do
907 a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800)
908
909 orig_user =
910 insert(
911 :user,
912 local: false,
913 nickname: "admin@mastodon.example.org",
914 ap_id: "http://mastodon.example.org/users/admin",
915 last_refreshed_at: a_week_ago
916 )
917
918 assert orig_user.last_refreshed_at == a_week_ago
919
920 {:ok, user} = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/admin")
921
922 assert user.inbox
923
924 refute user.last_refreshed_at == orig_user.last_refreshed_at
925 end
926
927 test "if nicknames clash, the old user gets a prefix with the old id to the nickname" do
928 a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800)
929
930 orig_user =
931 insert(
932 :user,
933 local: false,
934 nickname: "admin@mastodon.example.org",
935 ap_id: "http://mastodon.example.org/users/harinezumigari",
936 last_refreshed_at: a_week_ago
937 )
938
939 assert orig_user.last_refreshed_at == a_week_ago
940
941 {:ok, user} = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/admin")
942
943 assert user.inbox
944
945 refute user.id == orig_user.id
946
947 orig_user = User.get_by_id(orig_user.id)
948
949 assert orig_user.nickname == "#{orig_user.id}.admin@mastodon.example.org"
950 end
951
952 @tag capture_log: true
953 test "it returns the old user if stale, but unfetchable" do
954 a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800)
955
956 orig_user =
957 insert(
958 :user,
959 local: false,
960 nickname: "admin@mastodon.example.org",
961 ap_id: "http://mastodon.example.org/users/raymoo",
962 last_refreshed_at: a_week_ago
963 )
964
965 assert orig_user.last_refreshed_at == a_week_ago
966
967 {:ok, user} = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/raymoo")
968
969 assert user.last_refreshed_at == orig_user.last_refreshed_at
970 end
971
972 test "it doesn't fail on invalid alsoKnownAs entries" do
973 Tesla.Mock.mock(fn
974 %{url: "https://mbp.example.com/"} ->
975 %Tesla.Env{
976 status: 200,
977 body:
978 "test/fixtures/microblogpub/user_with_invalid_also_known_as.json"
979 |> File.read!(),
980 headers: [{"content-type", "application/activity+json"}]
981 }
982
983 _ ->
984 %Tesla.Env{status: 404}
985 end)
986
987 assert {:ok, %User{also_known_as: []}} =
988 User.get_or_fetch_by_ap_id("https://mbp.example.com/")
989 end
990 end
991
992 test "returns an ap_id for a user" do
993 user = insert(:user)
994
995 assert User.ap_id(user) ==
996 Pleroma.Web.Router.Helpers.user_feed_url(
997 Pleroma.Web.Endpoint,
998 :feed_redirect,
999 user.nickname
1000 )
1001 end
1002
1003 test "returns an ap_followers link for a user" do
1004 user = insert(:user)
1005
1006 assert User.ap_followers(user) ==
1007 Pleroma.Web.Router.Helpers.user_feed_url(
1008 Pleroma.Web.Endpoint,
1009 :feed_redirect,
1010 user.nickname
1011 ) <> "/followers"
1012 end
1013
1014 describe "remote user changeset" do
1015 @valid_remote %{
1016 bio: "hello",
1017 name: "Someone",
1018 nickname: "a@b.de",
1019 ap_id: "http...",
1020 avatar: %{some: "avatar"}
1021 }
1022 setup do: clear_config([:instance, :user_bio_length])
1023 setup do: clear_config([:instance, :user_name_length])
1024
1025 test "it confirms validity" do
1026 cs = User.remote_user_changeset(@valid_remote)
1027 assert cs.valid?
1028 end
1029
1030 test "it sets the follower_adress" do
1031 cs = User.remote_user_changeset(@valid_remote)
1032 # remote users get a fake local follower address
1033 assert cs.changes.follower_address ==
1034 User.ap_followers(%User{nickname: @valid_remote[:nickname]})
1035 end
1036
1037 test "it enforces the fqn format for nicknames" do
1038 cs = User.remote_user_changeset(%{@valid_remote | nickname: "bla"})
1039 assert Ecto.Changeset.get_field(cs, :local) == false
1040 assert cs.changes.avatar
1041 refute cs.valid?
1042 end
1043
1044 test "it has required fields" do
1045 [:ap_id]
1046 |> Enum.each(fn field ->
1047 cs = User.remote_user_changeset(Map.delete(@valid_remote, field))
1048 refute cs.valid?
1049 end)
1050 end
1051
1052 test "it is invalid given a local user" do
1053 user = insert(:user)
1054 cs = User.remote_user_changeset(user, %{name: "tom from myspace"})
1055
1056 refute cs.valid?
1057 end
1058 end
1059
1060 describe "followers and friends" do
1061 test "gets all followers for a given user" do
1062 user = insert(:user)
1063 follower_one = insert(:user)
1064 follower_two = insert(:user)
1065 not_follower = insert(:user)
1066
1067 {:ok, follower_one, user} = User.follow(follower_one, user)
1068 {:ok, follower_two, user} = User.follow(follower_two, user)
1069
1070 res = User.get_followers(user)
1071
1072 assert Enum.member?(res, follower_one)
1073 assert Enum.member?(res, follower_two)
1074 refute Enum.member?(res, not_follower)
1075 end
1076
1077 test "gets all friends (followed users) for a given user" do
1078 user = insert(:user)
1079 followed_one = insert(:user)
1080 followed_two = insert(:user)
1081 not_followed = insert(:user)
1082
1083 {:ok, user, followed_one} = User.follow(user, followed_one)
1084 {:ok, user, followed_two} = User.follow(user, followed_two)
1085
1086 res = User.get_friends(user)
1087
1088 followed_one = User.get_cached_by_ap_id(followed_one.ap_id)
1089 followed_two = User.get_cached_by_ap_id(followed_two.ap_id)
1090 assert Enum.member?(res, followed_one)
1091 assert Enum.member?(res, followed_two)
1092 refute Enum.member?(res, not_followed)
1093 end
1094 end
1095
1096 describe "updating note and follower count" do
1097 test "it sets the note_count property" do
1098 note = insert(:note)
1099
1100 user = User.get_cached_by_ap_id(note.data["actor"])
1101
1102 assert user.note_count == 0
1103
1104 {:ok, user} = User.update_note_count(user)
1105
1106 assert user.note_count == 1
1107 end
1108
1109 test "it increases the note_count property" do
1110 note = insert(:note)
1111 user = User.get_cached_by_ap_id(note.data["actor"])
1112
1113 assert user.note_count == 0
1114
1115 {:ok, user} = User.increase_note_count(user)
1116
1117 assert user.note_count == 1
1118
1119 {:ok, user} = User.increase_note_count(user)
1120
1121 assert user.note_count == 2
1122 end
1123
1124 test "it decreases the note_count property" do
1125 note = insert(:note)
1126 user = User.get_cached_by_ap_id(note.data["actor"])
1127
1128 assert user.note_count == 0
1129
1130 {:ok, user} = User.increase_note_count(user)
1131
1132 assert user.note_count == 1
1133
1134 {:ok, user} = User.decrease_note_count(user)
1135
1136 assert user.note_count == 0
1137
1138 {:ok, user} = User.decrease_note_count(user)
1139
1140 assert user.note_count == 0
1141 end
1142
1143 test "it sets the follower_count property" do
1144 user = insert(:user)
1145 follower = insert(:user)
1146
1147 User.follow(follower, user)
1148
1149 assert user.follower_count == 0
1150
1151 {:ok, user} = User.update_follower_count(user)
1152
1153 assert user.follower_count == 1
1154 end
1155 end
1156
1157 describe "mutes" do
1158 test "it mutes people" do
1159 user = insert(:user)
1160 muted_user = insert(:user)
1161
1162 refute User.mutes?(user, muted_user)
1163 refute User.muted_notifications?(user, muted_user)
1164
1165 {:ok, _user_relationships} = User.mute(user, muted_user)
1166
1167 assert User.mutes?(user, muted_user)
1168 assert User.muted_notifications?(user, muted_user)
1169 end
1170
1171 test "expiring" do
1172 user = insert(:user)
1173 muted_user = insert(:user)
1174
1175 {:ok, _user_relationships} = User.mute(user, muted_user, %{expires_in: 60})
1176 assert User.mutes?(user, muted_user)
1177
1178 worker = Pleroma.Workers.MuteExpireWorker
1179 args = %{"op" => "unmute_user", "muter_id" => user.id, "mutee_id" => muted_user.id}
1180
1181 assert_enqueued(
1182 worker: worker,
1183 args: args
1184 )
1185
1186 assert :ok = perform_job(worker, args)
1187
1188 refute User.mutes?(user, muted_user)
1189 refute User.muted_notifications?(user, muted_user)
1190 end
1191
1192 test "it unmutes users" do
1193 user = insert(:user)
1194 muted_user = insert(:user)
1195
1196 {:ok, _user_relationships} = User.mute(user, muted_user)
1197 {:ok, _user_mute} = User.unmute(user, muted_user)
1198
1199 refute User.mutes?(user, muted_user)
1200 refute User.muted_notifications?(user, muted_user)
1201 end
1202
1203 test "it unmutes users by id" do
1204 user = insert(:user)
1205 muted_user = insert(:user)
1206
1207 {:ok, _user_relationships} = User.mute(user, muted_user)
1208 {:ok, _user_mute} = User.unmute(user.id, muted_user.id)
1209
1210 refute User.mutes?(user, muted_user)
1211 refute User.muted_notifications?(user, muted_user)
1212 end
1213
1214 test "it mutes user without notifications" do
1215 user = insert(:user)
1216 muted_user = insert(:user)
1217
1218 refute User.mutes?(user, muted_user)
1219 refute User.muted_notifications?(user, muted_user)
1220
1221 {:ok, _user_relationships} = User.mute(user, muted_user, %{notifications: false})
1222
1223 assert User.mutes?(user, muted_user)
1224 refute User.muted_notifications?(user, muted_user)
1225 end
1226 end
1227
1228 describe "blocks" do
1229 test "it blocks people" do
1230 user = insert(:user)
1231 blocked_user = insert(:user)
1232
1233 refute User.blocks?(user, blocked_user)
1234
1235 {:ok, _user_relationship} = User.block(user, blocked_user)
1236
1237 assert User.blocks?(user, blocked_user)
1238 end
1239
1240 test "it unblocks users" do
1241 user = insert(:user)
1242 blocked_user = insert(:user)
1243
1244 {:ok, _user_relationship} = User.block(user, blocked_user)
1245 {:ok, _user_block} = User.unblock(user, blocked_user)
1246
1247 refute User.blocks?(user, blocked_user)
1248 end
1249
1250 test "blocks tear down cyclical follow relationships" do
1251 blocker = insert(:user)
1252 blocked = insert(:user)
1253
1254 {:ok, blocker, blocked} = User.follow(blocker, blocked)
1255 {:ok, blocked, blocker} = User.follow(blocked, blocker)
1256
1257 assert User.following?(blocker, blocked)
1258 assert User.following?(blocked, blocker)
1259
1260 {:ok, _user_relationship} = User.block(blocker, blocked)
1261 blocked = User.get_cached_by_id(blocked.id)
1262
1263 assert User.blocks?(blocker, blocked)
1264
1265 refute User.following?(blocker, blocked)
1266 refute User.following?(blocked, blocker)
1267 end
1268
1269 test "blocks tear down blocker->blocked follow relationships" do
1270 blocker = insert(:user)
1271 blocked = insert(:user)
1272
1273 {:ok, blocker, blocked} = User.follow(blocker, blocked)
1274
1275 assert User.following?(blocker, blocked)
1276 refute User.following?(blocked, blocker)
1277
1278 {:ok, _user_relationship} = User.block(blocker, blocked)
1279 blocked = User.get_cached_by_id(blocked.id)
1280
1281 assert User.blocks?(blocker, blocked)
1282
1283 refute User.following?(blocker, blocked)
1284 refute User.following?(blocked, blocker)
1285 end
1286
1287 test "blocks tear down blocked->blocker follow relationships" do
1288 blocker = insert(:user)
1289 blocked = insert(:user)
1290
1291 {:ok, blocked, blocker} = User.follow(blocked, blocker)
1292
1293 refute User.following?(blocker, blocked)
1294 assert User.following?(blocked, blocker)
1295
1296 {:ok, _user_relationship} = User.block(blocker, blocked)
1297 blocked = User.get_cached_by_id(blocked.id)
1298
1299 assert User.blocks?(blocker, blocked)
1300
1301 refute User.following?(blocker, blocked)
1302 refute User.following?(blocked, blocker)
1303 end
1304
1305 test "blocks tear down blocked->blocker subscription relationships" do
1306 blocker = insert(:user)
1307 blocked = insert(:user)
1308
1309 {:ok, _subscription} = User.subscribe(blocked, blocker)
1310
1311 assert User.subscribed_to?(blocked, blocker)
1312 refute User.subscribed_to?(blocker, blocked)
1313
1314 {:ok, _user_relationship} = User.block(blocker, blocked)
1315
1316 assert User.blocks?(blocker, blocked)
1317 refute User.subscribed_to?(blocker, blocked)
1318 refute User.subscribed_to?(blocked, blocker)
1319 end
1320 end
1321
1322 describe "domain blocking" do
1323 test "blocks domains" do
1324 user = insert(:user)
1325 collateral_user = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
1326
1327 {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
1328
1329 assert User.blocks?(user, collateral_user)
1330 end
1331
1332 test "does not block domain with same end" do
1333 user = insert(:user)
1334
1335 collateral_user =
1336 insert(:user, %{ap_id: "https://another-awful-and-rude-instance.com/user/bully"})
1337
1338 {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
1339
1340 refute User.blocks?(user, collateral_user)
1341 end
1342
1343 test "does not block domain with same end if wildcard added" do
1344 user = insert(:user)
1345
1346 collateral_user =
1347 insert(:user, %{ap_id: "https://another-awful-and-rude-instance.com/user/bully"})
1348
1349 {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
1350
1351 refute User.blocks?(user, collateral_user)
1352 end
1353
1354 test "blocks domain with wildcard for subdomain" do
1355 user = insert(:user)
1356
1357 user_from_subdomain =
1358 insert(:user, %{ap_id: "https://subdomain.awful-and-rude-instance.com/user/bully"})
1359
1360 user_with_two_subdomains =
1361 insert(:user, %{
1362 ap_id: "https://subdomain.second_subdomain.awful-and-rude-instance.com/user/bully"
1363 })
1364
1365 user_domain = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
1366
1367 {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
1368
1369 assert User.blocks?(user, user_from_subdomain)
1370 assert User.blocks?(user, user_with_two_subdomains)
1371 assert User.blocks?(user, user_domain)
1372 end
1373
1374 test "unblocks domains" do
1375 user = insert(:user)
1376 collateral_user = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
1377
1378 {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
1379 {:ok, user} = User.unblock_domain(user, "awful-and-rude-instance.com")
1380
1381 refute User.blocks?(user, collateral_user)
1382 end
1383
1384 test "follows take precedence over domain blocks" do
1385 user = insert(:user)
1386 good_eggo = insert(:user, %{ap_id: "https://meanies.social/user/cuteposter"})
1387
1388 {:ok, user} = User.block_domain(user, "meanies.social")
1389 {:ok, user, good_eggo} = User.follow(user, good_eggo)
1390
1391 refute User.blocks?(user, good_eggo)
1392 end
1393 end
1394
1395 describe "get_recipients_from_activity" do
1396 test "works for announces" do
1397 actor = insert(:user)
1398 user = insert(:user, local: true)
1399
1400 {:ok, activity} = CommonAPI.post(actor, %{status: "hello"})
1401 {:ok, announce} = CommonAPI.repeat(activity.id, user)
1402
1403 recipients = User.get_recipients_from_activity(announce)
1404
1405 assert user in recipients
1406 end
1407
1408 test "get recipients" do
1409 actor = insert(:user)
1410 user = insert(:user, local: true)
1411 user_two = insert(:user, local: false)
1412 addressed = insert(:user, local: true)
1413 addressed_remote = insert(:user, local: false)
1414
1415 {:ok, activity} =
1416 CommonAPI.post(actor, %{
1417 status: "hey @#{addressed.nickname} @#{addressed_remote.nickname}"
1418 })
1419
1420 assert Enum.map([actor, addressed], & &1.ap_id) --
1421 Enum.map(User.get_recipients_from_activity(activity), & &1.ap_id) == []
1422
1423 {:ok, user, actor} = User.follow(user, actor)
1424 {:ok, _user_two, _actor} = User.follow(user_two, actor)
1425 recipients = User.get_recipients_from_activity(activity)
1426 assert length(recipients) == 3
1427 assert user in recipients
1428 assert addressed in recipients
1429 end
1430
1431 test "has following" do
1432 actor = insert(:user)
1433 user = insert(:user)
1434 user_two = insert(:user)
1435 addressed = insert(:user, local: true)
1436
1437 {:ok, activity} =
1438 CommonAPI.post(actor, %{
1439 status: "hey @#{addressed.nickname}"
1440 })
1441
1442 assert Enum.map([actor, addressed], & &1.ap_id) --
1443 Enum.map(User.get_recipients_from_activity(activity), & &1.ap_id) == []
1444
1445 {:ok, _actor, _user} = User.follow(actor, user)
1446 {:ok, _actor, _user_two} = User.follow(actor, user_two)
1447 recipients = User.get_recipients_from_activity(activity)
1448 assert length(recipients) == 2
1449 assert addressed in recipients
1450 end
1451 end
1452
1453 describe ".set_activation" do
1454 test "can de-activate then re-activate a user" do
1455 user = insert(:user)
1456 assert user.is_active
1457 {:ok, user} = User.set_activation(user, false)
1458 refute user.is_active
1459 {:ok, user} = User.set_activation(user, true)
1460 assert user.is_active
1461 end
1462
1463 test "hide a user from followers" do
1464 user = insert(:user)
1465 user2 = insert(:user)
1466
1467 {:ok, user, user2} = User.follow(user, user2)
1468 {:ok, _user} = User.set_activation(user, false)
1469
1470 user2 = User.get_cached_by_id(user2.id)
1471
1472 assert user2.follower_count == 0
1473 assert [] = User.get_followers(user2)
1474 end
1475
1476 test "hide a user from friends" do
1477 user = insert(:user)
1478 user2 = insert(:user)
1479
1480 {:ok, user2, user} = User.follow(user2, user)
1481 assert user2.following_count == 1
1482 assert User.following_count(user2) == 1
1483
1484 {:ok, _user} = User.set_activation(user, false)
1485
1486 user2 = User.get_cached_by_id(user2.id)
1487
1488 assert refresh_record(user2).following_count == 0
1489 assert user2.following_count == 0
1490 assert User.following_count(user2) == 0
1491 assert [] = User.get_friends(user2)
1492 end
1493
1494 test "hide a user's statuses from timelines and notifications" do
1495 user = insert(:user)
1496 user2 = insert(:user)
1497
1498 {:ok, user2, user} = User.follow(user2, user)
1499
1500 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{user2.nickname}"})
1501
1502 activity = Repo.preload(activity, :bookmark)
1503
1504 [notification] = Pleroma.Notification.for_user(user2)
1505 assert notification.activity.id == activity.id
1506
1507 assert [activity] == ActivityPub.fetch_public_activities(%{}) |> Repo.preload(:bookmark)
1508
1509 assert [%{activity | thread_muted?: CommonAPI.thread_muted?(user2, activity)}] ==
1510 ActivityPub.fetch_activities([user2.ap_id | User.following(user2)], %{
1511 user: user2
1512 })
1513
1514 {:ok, _user} = User.set_activation(user, false)
1515
1516 assert [] == ActivityPub.fetch_public_activities(%{})
1517 assert [] == Pleroma.Notification.for_user(user2)
1518
1519 assert [] ==
1520 ActivityPub.fetch_activities([user2.ap_id | User.following(user2)], %{
1521 user: user2
1522 })
1523 end
1524 end
1525
1526 describe "approve" do
1527 test "approves a user" do
1528 user = insert(:user, is_approved: false)
1529 refute user.is_approved
1530 {:ok, user} = User.approve(user)
1531 assert user.is_approved
1532 end
1533
1534 test "approves a list of users" do
1535 unapproved_users = [
1536 insert(:user, is_approved: false),
1537 insert(:user, is_approved: false),
1538 insert(:user, is_approved: false)
1539 ]
1540
1541 {:ok, users} = User.approve(unapproved_users)
1542
1543 assert Enum.count(users) == 3
1544
1545 Enum.each(users, fn user ->
1546 assert user.is_approved
1547 end)
1548 end
1549
1550 test "it sends welcome email if it is set" do
1551 clear_config([:welcome, :email, :enabled], true)
1552 clear_config([:welcome, :email, :sender], "tester@test.me")
1553
1554 user = insert(:user, is_approved: false)
1555 welcome_user = insert(:user, email: "tester@test.me")
1556 instance_name = Pleroma.Config.get([:instance, :name])
1557
1558 User.approve(user)
1559
1560 ObanHelpers.perform_all()
1561
1562 assert_email_sent(
1563 from: {instance_name, welcome_user.email},
1564 to: {user.name, user.email},
1565 html_body: "Welcome to #{instance_name}"
1566 )
1567 end
1568
1569 test "approving an approved user does not trigger post-register actions" do
1570 clear_config([:welcome, :email, :enabled], true)
1571
1572 user = insert(:user, is_approved: true)
1573 User.approve(user)
1574
1575 ObanHelpers.perform_all()
1576
1577 assert_no_email_sent()
1578 end
1579 end
1580
1581 describe "confirm" do
1582 test "confirms a user" do
1583 user = insert(:user, is_confirmed: false)
1584 refute user.is_confirmed
1585 {:ok, user} = User.confirm(user)
1586 assert user.is_confirmed
1587 end
1588
1589 test "confirms a list of users" do
1590 unconfirmed_users = [
1591 insert(:user, is_confirmed: false),
1592 insert(:user, is_confirmed: false),
1593 insert(:user, is_confirmed: false)
1594 ]
1595
1596 {:ok, users} = User.confirm(unconfirmed_users)
1597
1598 assert Enum.count(users) == 3
1599
1600 Enum.each(users, fn user ->
1601 assert user.is_confirmed
1602 end)
1603 end
1604
1605 test "sends approval emails when `is_approved: false`" do
1606 admin = insert(:user, is_admin: true)
1607 user = insert(:user, is_confirmed: false, is_approved: false)
1608 User.confirm(user)
1609
1610 ObanHelpers.perform_all()
1611
1612 user_email = Pleroma.Emails.UserEmail.approval_pending_email(user)
1613 admin_email = Pleroma.Emails.AdminEmail.new_unapproved_registration(admin, user)
1614
1615 notify_email = Pleroma.Config.get([:instance, :notify_email])
1616 instance_name = Pleroma.Config.get([:instance, :name])
1617
1618 # User approval email
1619 assert_email_sent(
1620 from: {instance_name, notify_email},
1621 to: {user.name, user.email},
1622 html_body: user_email.html_body
1623 )
1624
1625 # Admin email
1626 assert_email_sent(
1627 from: {instance_name, notify_email},
1628 to: {admin.name, admin.email},
1629 html_body: admin_email.html_body
1630 )
1631 end
1632
1633 test "confirming a confirmed user does not trigger post-register actions" do
1634 user = insert(:user, is_confirmed: true, is_approved: false)
1635 User.confirm(user)
1636
1637 ObanHelpers.perform_all()
1638
1639 assert_no_email_sent()
1640 end
1641 end
1642
1643 describe "delete" do
1644 setup do
1645 {:ok, user} = insert(:user) |> User.set_cache()
1646
1647 [user: user]
1648 end
1649
1650 setup do: clear_config([:instance, :federating])
1651
1652 test ".delete_user_activities deletes all create activities", %{user: user} do
1653 {:ok, activity} = CommonAPI.post(user, %{status: "2hu"})
1654
1655 User.delete_user_activities(user)
1656
1657 # TODO: Test removal favorites, repeats, delete activities.
1658 refute Activity.get_by_id(activity.id)
1659 end
1660
1661 test "it deactivates a user, all follow relationships and all activities", %{user: user} do
1662 follower = insert(:user)
1663 {:ok, follower, user} = User.follow(follower, user)
1664
1665 locked_user = insert(:user, name: "locked", is_locked: true)
1666 {:ok, _, _} = User.follow(user, locked_user, :follow_pending)
1667
1668 object = insert(:note, user: user)
1669 activity = insert(:note_activity, user: user, note: object)
1670
1671 object_two = insert(:note, user: follower)
1672 activity_two = insert(:note_activity, user: follower, note: object_two)
1673
1674 {:ok, like} = CommonAPI.favorite(user, activity_two.id)
1675 {:ok, like_two} = CommonAPI.favorite(follower, activity.id)
1676 {:ok, repeat} = CommonAPI.repeat(activity_two.id, user)
1677
1678 {:ok, job} = User.delete(user)
1679 {:ok, _user} = ObanHelpers.perform(job)
1680
1681 follower = User.get_cached_by_id(follower.id)
1682
1683 refute User.following?(follower, user)
1684 assert %{is_active: false} = User.get_by_id(user.id)
1685
1686 assert [] == User.get_follow_requests(locked_user)
1687
1688 user_activities =
1689 user.ap_id
1690 |> Activity.Queries.by_actor()
1691 |> Repo.all()
1692 |> Enum.map(fn act -> act.data["type"] end)
1693
1694 assert Enum.all?(user_activities, fn act -> act in ~w(Delete Undo) end)
1695
1696 refute Activity.get_by_id(activity.id)
1697 refute Activity.get_by_id(like.id)
1698 refute Activity.get_by_id(like_two.id)
1699 refute Activity.get_by_id(repeat.id)
1700 end
1701 end
1702
1703 test "delete/1 when confirmation is pending deletes the user" do
1704 clear_config([:instance, :account_activation_required], true)
1705 user = insert(:user, is_confirmed: false)
1706
1707 {:ok, job} = User.delete(user)
1708 {:ok, _} = ObanHelpers.perform(job)
1709
1710 refute User.get_cached_by_id(user.id)
1711 refute User.get_by_id(user.id)
1712 end
1713
1714 test "delete/1 when approval is pending deletes the user" do
1715 user = insert(:user, is_approved: false)
1716
1717 {:ok, job} = User.delete(user)
1718 {:ok, _} = ObanHelpers.perform(job)
1719
1720 refute User.get_cached_by_id(user.id)
1721 refute User.get_by_id(user.id)
1722 end
1723
1724 test "delete/1 purges a user when they wouldn't be fully deleted" do
1725 user =
1726 insert(:user, %{
1727 bio: "eyy lmao",
1728 name: "qqqqqqq",
1729 password_hash: "pdfk2$1b3n159001",
1730 keys: "RSA begin buplic key",
1731 public_key: "--PRIVATE KEYE--",
1732 avatar: %{"a" => "b"},
1733 tags: ["qqqqq"],
1734 banner: %{"a" => "b"},
1735 background: %{"a" => "b"},
1736 note_count: 9,
1737 follower_count: 9,
1738 following_count: 9001,
1739 is_locked: true,
1740 is_confirmed: true,
1741 password_reset_pending: true,
1742 is_approved: true,
1743 registration_reason: "ahhhhh",
1744 confirmation_token: "qqqq",
1745 domain_blocks: ["lain.com"],
1746 is_active: false,
1747 ap_enabled: true,
1748 is_moderator: true,
1749 is_admin: true,
1750 mastofe_settings: %{"a" => "b"},
1751 mascot: %{"a" => "b"},
1752 emoji: %{"a" => "b"},
1753 pleroma_settings_store: %{"q" => "x"},
1754 fields: [%{"gg" => "qq"}],
1755 raw_fields: [%{"gg" => "qq"}],
1756 is_discoverable: true,
1757 also_known_as: ["https://lol.olo/users/loll"]
1758 })
1759
1760 {:ok, job} = User.delete(user)
1761 {:ok, _} = ObanHelpers.perform(job)
1762 user = User.get_by_id(user.id)
1763
1764 assert %User{
1765 bio: "",
1766 raw_bio: nil,
1767 email: nil,
1768 name: nil,
1769 password_hash: nil,
1770 keys: "RSA begin buplic key",
1771 public_key: "--PRIVATE KEYE--",
1772 avatar: %{},
1773 tags: [],
1774 last_refreshed_at: nil,
1775 last_digest_emailed_at: nil,
1776 banner: %{},
1777 background: %{},
1778 note_count: 0,
1779 follower_count: 0,
1780 following_count: 0,
1781 is_locked: false,
1782 is_confirmed: true,
1783 password_reset_pending: false,
1784 is_approved: true,
1785 registration_reason: nil,
1786 confirmation_token: nil,
1787 domain_blocks: [],
1788 is_active: false,
1789 ap_enabled: false,
1790 is_moderator: false,
1791 is_admin: false,
1792 mastofe_settings: nil,
1793 mascot: nil,
1794 emoji: %{},
1795 pleroma_settings_store: %{},
1796 fields: [],
1797 raw_fields: [],
1798 is_discoverable: false,
1799 also_known_as: []
1800 } = user
1801 end
1802
1803 test "delete/1 purges a remote user" do
1804 user =
1805 insert(:user, %{
1806 name: "qqqqqqq",
1807 avatar: %{"a" => "b"},
1808 banner: %{"a" => "b"},
1809 local: false
1810 })
1811
1812 {:ok, job} = User.delete(user)
1813 {:ok, _} = ObanHelpers.perform(job)
1814 user = User.get_by_id(user.id)
1815
1816 assert user.name == nil
1817 assert user.avatar == %{}
1818 assert user.banner == %{}
1819 end
1820
1821 describe "set_suggestion" do
1822 test "suggests a user" do
1823 user = insert(:user, is_suggested: false)
1824 refute user.is_suggested
1825 {:ok, user} = User.set_suggestion(user, true)
1826 assert user.is_suggested
1827 end
1828
1829 test "suggests a list of users" do
1830 unsuggested_users = [
1831 insert(:user, is_suggested: false),
1832 insert(:user, is_suggested: false),
1833 insert(:user, is_suggested: false)
1834 ]
1835
1836 {:ok, users} = User.set_suggestion(unsuggested_users, true)
1837
1838 assert Enum.count(users) == 3
1839
1840 Enum.each(users, fn user ->
1841 assert user.is_suggested
1842 end)
1843 end
1844
1845 test "unsuggests a user" do
1846 user = insert(:user, is_suggested: true)
1847 assert user.is_suggested
1848 {:ok, user} = User.set_suggestion(user, false)
1849 refute user.is_suggested
1850 end
1851 end
1852
1853 test "get_public_key_for_ap_id fetches a user that's not in the db" do
1854 assert {:ok, _key} = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin")
1855 end
1856
1857 describe "per-user rich-text filtering" do
1858 test "html_filter_policy returns default policies, when rich-text is enabled" do
1859 user = insert(:user)
1860
1861 assert Pleroma.Config.get([:markup, :scrub_policy]) == User.html_filter_policy(user)
1862 end
1863
1864 test "html_filter_policy returns TwitterText scrubber when rich-text is disabled" do
1865 user = insert(:user, no_rich_text: true)
1866
1867 assert Pleroma.HTML.Scrubber.TwitterText == User.html_filter_policy(user)
1868 end
1869 end
1870
1871 describe "caching" do
1872 test "invalidate_cache works" do
1873 user = insert(:user)
1874
1875 User.set_cache(user)
1876 User.invalidate_cache(user)
1877
1878 {:ok, nil} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
1879 {:ok, nil} = Cachex.get(:user_cache, "nickname:#{user.nickname}")
1880 end
1881
1882 test "User.delete() plugs any possible zombie objects" do
1883 user = insert(:user)
1884
1885 {:ok, job} = User.delete(user)
1886 {:ok, _} = ObanHelpers.perform(job)
1887
1888 {:ok, cached_user} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
1889
1890 assert cached_user != user
1891
1892 {:ok, cached_user} = Cachex.get(:user_cache, "nickname:#{user.ap_id}")
1893
1894 assert cached_user != user
1895 end
1896 end
1897
1898 describe "account_status/1" do
1899 setup do: clear_config([:instance, :account_activation_required])
1900
1901 test "return confirmation_pending for unconfirm user" do
1902 clear_config([:instance, :account_activation_required], true)
1903 user = insert(:user, is_confirmed: false)
1904 assert User.account_status(user) == :confirmation_pending
1905 end
1906
1907 test "return active for confirmed user" do
1908 clear_config([:instance, :account_activation_required], true)
1909 user = insert(:user, is_confirmed: true)
1910 assert User.account_status(user) == :active
1911 end
1912
1913 test "return active for remote user" do
1914 user = insert(:user, local: false)
1915 assert User.account_status(user) == :active
1916 end
1917
1918 test "returns :password_reset_pending for user with reset password" do
1919 user = insert(:user, password_reset_pending: true)
1920 assert User.account_status(user) == :password_reset_pending
1921 end
1922
1923 test "returns :deactivated for deactivated user" do
1924 user = insert(:user, local: true, is_confirmed: true, is_active: false)
1925 assert User.account_status(user) == :deactivated
1926 end
1927
1928 test "returns :approval_pending for unapproved user" do
1929 user = insert(:user, local: true, is_approved: false)
1930 assert User.account_status(user) == :approval_pending
1931
1932 user = insert(:user, local: true, is_confirmed: false, is_approved: false)
1933 assert User.account_status(user) == :approval_pending
1934 end
1935 end
1936
1937 describe "superuser?/1" do
1938 test "returns false for unprivileged users" do
1939 user = insert(:user, local: true)
1940
1941 refute User.superuser?(user)
1942 end
1943
1944 test "returns false for remote users" do
1945 user = insert(:user, local: false)
1946 remote_admin_user = insert(:user, local: false, is_admin: true)
1947
1948 refute User.superuser?(user)
1949 refute User.superuser?(remote_admin_user)
1950 end
1951
1952 test "returns true for local moderators" do
1953 user = insert(:user, local: true, is_moderator: true)
1954
1955 assert User.superuser?(user)
1956 end
1957
1958 test "returns true for local admins" do
1959 user = insert(:user, local: true, is_admin: true)
1960
1961 assert User.superuser?(user)
1962 end
1963 end
1964
1965 describe "invisible?/1" do
1966 test "returns true for an invisible user" do
1967 user = insert(:user, local: true, invisible: true)
1968
1969 assert User.invisible?(user)
1970 end
1971
1972 test "returns false for a non-invisible user" do
1973 user = insert(:user, local: true)
1974
1975 refute User.invisible?(user)
1976 end
1977 end
1978
1979 describe "visible_for/2" do
1980 test "returns true when the account is itself" do
1981 user = insert(:user, local: true)
1982
1983 assert User.visible_for(user, user) == :visible
1984 end
1985
1986 test "returns false when the account is unconfirmed and confirmation is required" do
1987 clear_config([:instance, :account_activation_required], true)
1988
1989 user = insert(:user, local: true, is_confirmed: false)
1990 other_user = insert(:user, local: true)
1991
1992 refute User.visible_for(user, other_user) == :visible
1993 end
1994
1995 test "returns true when the account is unconfirmed and confirmation is required but the account is remote" do
1996 clear_config([:instance, :account_activation_required], true)
1997
1998 user = insert(:user, local: false, is_confirmed: false)
1999 other_user = insert(:user, local: true)
2000
2001 assert User.visible_for(user, other_user) == :visible
2002 end
2003
2004 test "returns true when the account is unconfirmed and being viewed by a privileged account (confirmation required)" do
2005 clear_config([:instance, :account_activation_required], true)
2006
2007 user = insert(:user, local: true, is_confirmed: false)
2008 other_user = insert(:user, local: true, is_admin: true)
2009
2010 assert User.visible_for(user, other_user) == :visible
2011 end
2012 end
2013
2014 describe "parse_bio/2" do
2015 test "preserves hosts in user links text" do
2016 remote_user = insert(:user, local: false, nickname: "nick@domain.com")
2017 user = insert(:user)
2018 bio = "A.k.a. @nick@domain.com"
2019
2020 expected_text =
2021 ~s(A.k.a. <span class="h-card"><a class="u-url mention" data-user="#{remote_user.id}" href="#{remote_user.ap_id}" rel="ugc">@<span>nick@domain.com</span></a></span>)
2022
2023 assert expected_text == User.parse_bio(bio, user)
2024 end
2025
2026 test "Adds rel=me on linkbacked urls" do
2027 user = insert(:user, ap_id: "https://social.example.org/users/lain")
2028
2029 bio = "http://example.com/rel_me/null"
2030 expected_text = "<a href=\"#{bio}\">#{bio}</a>"
2031 assert expected_text == User.parse_bio(bio, user)
2032
2033 bio = "http://example.com/rel_me/link"
2034 expected_text = "<a href=\"#{bio}\" rel=\"me\">#{bio}</a>"
2035 assert expected_text == User.parse_bio(bio, user)
2036
2037 bio = "http://example.com/rel_me/anchor"
2038 expected_text = "<a href=\"#{bio}\" rel=\"me\">#{bio}</a>"
2039 assert expected_text == User.parse_bio(bio, user)
2040 end
2041 end
2042
2043 test "follower count is updated when a follower is blocked" do
2044 user = insert(:user)
2045 follower = insert(:user)
2046 follower2 = insert(:user)
2047 follower3 = insert(:user)
2048
2049 {:ok, follower, user} = User.follow(follower, user)
2050 {:ok, _follower2, _user} = User.follow(follower2, user)
2051 {:ok, _follower3, _user} = User.follow(follower3, user)
2052
2053 {:ok, _user_relationship} = User.block(user, follower)
2054 user = refresh_record(user)
2055
2056 assert user.follower_count == 2
2057 end
2058
2059 describe "list_inactive_users_query/1" do
2060 defp days_ago(days) do
2061 NaiveDateTime.add(
2062 NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second),
2063 -days * 60 * 60 * 24,
2064 :second
2065 )
2066 end
2067
2068 test "Users are inactive by default" do
2069 total = 10
2070
2071 users =
2072 Enum.map(1..total, fn _ ->
2073 insert(:user, last_digest_emailed_at: days_ago(20), is_active: true)
2074 end)
2075
2076 inactive_users_ids =
2077 Pleroma.User.list_inactive_users_query()
2078 |> Pleroma.Repo.all()
2079 |> Enum.map(& &1.id)
2080
2081 Enum.each(users, fn user ->
2082 assert user.id in inactive_users_ids
2083 end)
2084 end
2085
2086 test "Only includes users who has no recent activity" do
2087 total = 10
2088
2089 users =
2090 Enum.map(1..total, fn _ ->
2091 insert(:user, last_digest_emailed_at: days_ago(20), is_active: true)
2092 end)
2093
2094 {inactive, active} = Enum.split(users, trunc(total / 2))
2095
2096 Enum.map(active, fn user ->
2097 to = Enum.random(users -- [user])
2098
2099 {:ok, _} =
2100 CommonAPI.post(user, %{
2101 status: "hey @#{to.nickname}"
2102 })
2103 end)
2104
2105 inactive_users_ids =
2106 Pleroma.User.list_inactive_users_query()
2107 |> Pleroma.Repo.all()
2108 |> Enum.map(& &1.id)
2109
2110 Enum.each(active, fn user ->
2111 refute user.id in inactive_users_ids
2112 end)
2113
2114 Enum.each(inactive, fn user ->
2115 assert user.id in inactive_users_ids
2116 end)
2117 end
2118
2119 test "Only includes users with no read notifications" do
2120 total = 10
2121
2122 users =
2123 Enum.map(1..total, fn _ ->
2124 insert(:user, last_digest_emailed_at: days_ago(20), is_active: true)
2125 end)
2126
2127 [sender | recipients] = users
2128 {inactive, active} = Enum.split(recipients, trunc(total / 2))
2129
2130 Enum.each(recipients, fn to ->
2131 {:ok, _} =
2132 CommonAPI.post(sender, %{
2133 status: "hey @#{to.nickname}"
2134 })
2135
2136 {:ok, _} =
2137 CommonAPI.post(sender, %{
2138 status: "hey again @#{to.nickname}"
2139 })
2140 end)
2141
2142 Enum.each(active, fn user ->
2143 [n1, _n2] = Pleroma.Notification.for_user(user)
2144 {:ok, _} = Pleroma.Notification.read_one(user, n1.id)
2145 end)
2146
2147 inactive_users_ids =
2148 Pleroma.User.list_inactive_users_query()
2149 |> Pleroma.Repo.all()
2150 |> Enum.map(& &1.id)
2151
2152 Enum.each(active, fn user ->
2153 refute user.id in inactive_users_ids
2154 end)
2155
2156 Enum.each(inactive, fn user ->
2157 assert user.id in inactive_users_ids
2158 end)
2159 end
2160 end
2161
2162 describe "get_ap_ids_by_nicknames" do
2163 test "it returns a list of AP ids for a given set of nicknames" do
2164 user = insert(:user)
2165 user_two = insert(:user)
2166
2167 ap_ids = User.get_ap_ids_by_nicknames([user.nickname, user_two.nickname, "nonexistent"])
2168 assert length(ap_ids) == 2
2169 assert user.ap_id in ap_ids
2170 assert user_two.ap_id in ap_ids
2171 end
2172 end
2173
2174 describe "sync followers count" do
2175 setup do
2176 user1 = insert(:user, local: false, ap_id: "http://localhost:4001/users/masto_closed")
2177 user2 = insert(:user, local: false, ap_id: "http://localhost:4001/users/fuser2")
2178 insert(:user, local: true)
2179 insert(:user, local: false, is_active: false)
2180 {:ok, user1: user1, user2: user2}
2181 end
2182
2183 test "external_users/1 external active users with limit", %{user1: user1, user2: user2} do
2184 [fdb_user1] = User.external_users(limit: 1)
2185
2186 assert fdb_user1.ap_id
2187 assert fdb_user1.ap_id == user1.ap_id
2188 assert fdb_user1.id == user1.id
2189
2190 [fdb_user2] = User.external_users(max_id: fdb_user1.id, limit: 1)
2191
2192 assert fdb_user2.ap_id
2193 assert fdb_user2.ap_id == user2.ap_id
2194 assert fdb_user2.id == user2.id
2195
2196 assert User.external_users(max_id: fdb_user2.id, limit: 1) == []
2197 end
2198 end
2199
2200 describe "is_internal_user?/1" do
2201 test "non-internal user returns false" do
2202 user = insert(:user)
2203 refute User.is_internal_user?(user)
2204 end
2205
2206 test "user with no nickname returns true" do
2207 user = insert(:user, %{nickname: nil})
2208 assert User.is_internal_user?(user)
2209 end
2210
2211 test "user with internal-prefixed nickname returns true" do
2212 user = insert(:user, %{nickname: "internal.test"})
2213 assert User.is_internal_user?(user)
2214 end
2215 end
2216
2217 describe "update_and_set_cache/1" do
2218 test "returns error when user is stale instead Ecto.StaleEntryError" do
2219 user = insert(:user)
2220
2221 changeset = Ecto.Changeset.change(user, bio: "test")
2222
2223 Repo.delete(user)
2224
2225 assert {:error, %Ecto.Changeset{errors: [id: {"is stale", [stale: true]}], valid?: false}} =
2226 User.update_and_set_cache(changeset)
2227 end
2228
2229 test "performs update cache if user updated" do
2230 user = insert(:user)
2231 assert {:ok, nil} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
2232
2233 changeset = Ecto.Changeset.change(user, bio: "test-bio")
2234
2235 assert {:ok, %User{bio: "test-bio"} = user} = User.update_and_set_cache(changeset)
2236 assert {:ok, user} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
2237 assert %User{bio: "test-bio"} = User.get_cached_by_ap_id(user.ap_id)
2238 end
2239
2240 test "removes report notifs when user isn't superuser any more" do
2241 report_activity = insert(:report_activity)
2242 user = insert(:user, is_moderator: true, is_admin: true)
2243 {:ok, _} = Notification.create_notifications(report_activity)
2244
2245 assert [%Pleroma.Notification{type: "pleroma:report"}] = Notification.for_user(user)
2246
2247 {:ok, user} = user |> User.admin_api_update(%{is_moderator: false})
2248 # is still superuser because still admin
2249 assert [%Pleroma.Notification{type: "pleroma:report"}] = Notification.for_user(user)
2250
2251 {:ok, user} = user |> User.admin_api_update(%{is_moderator: true, is_admin: false})
2252 # is still superuser because still moderator
2253 assert [%Pleroma.Notification{type: "pleroma:report"}] = Notification.for_user(user)
2254
2255 {:ok, user} = user |> User.admin_api_update(%{is_moderator: false})
2256 # is not a superuser any more
2257 assert [] = Notification.for_user(user)
2258 end
2259 end
2260
2261 describe "following/followers synchronization" do
2262 setup do: clear_config([:instance, :external_user_synchronization])
2263
2264 test "updates the counters normally on following/getting a follow when disabled" do
2265 clear_config([:instance, :external_user_synchronization], false)
2266 user = insert(:user)
2267
2268 other_user =
2269 insert(:user,
2270 local: false,
2271 follower_address: "http://localhost:4001/users/masto_closed/followers",
2272 following_address: "http://localhost:4001/users/masto_closed/following",
2273 ap_enabled: true
2274 )
2275
2276 assert other_user.following_count == 0
2277 assert other_user.follower_count == 0
2278
2279 {:ok, user, other_user} = Pleroma.User.follow(user, other_user)
2280
2281 assert user.following_count == 1
2282 assert other_user.follower_count == 1
2283 end
2284
2285 test "synchronizes the counters with the remote instance for the followed when enabled" do
2286 clear_config([:instance, :external_user_synchronization], false)
2287
2288 user = insert(:user)
2289
2290 other_user =
2291 insert(:user,
2292 local: false,
2293 follower_address: "http://localhost:4001/users/masto_closed/followers",
2294 following_address: "http://localhost:4001/users/masto_closed/following",
2295 ap_enabled: true
2296 )
2297
2298 assert other_user.following_count == 0
2299 assert other_user.follower_count == 0
2300
2301 clear_config([:instance, :external_user_synchronization], true)
2302 {:ok, _user, other_user} = User.follow(user, other_user)
2303
2304 assert other_user.follower_count == 437
2305 end
2306
2307 test "synchronizes the counters with the remote instance for the follower when enabled" do
2308 clear_config([:instance, :external_user_synchronization], false)
2309
2310 user = insert(:user)
2311
2312 other_user =
2313 insert(:user,
2314 local: false,
2315 follower_address: "http://localhost:4001/users/masto_closed/followers",
2316 following_address: "http://localhost:4001/users/masto_closed/following",
2317 ap_enabled: true
2318 )
2319
2320 assert other_user.following_count == 0
2321 assert other_user.follower_count == 0
2322
2323 clear_config([:instance, :external_user_synchronization], true)
2324 {:ok, other_user, _user} = User.follow(other_user, user)
2325
2326 assert other_user.following_count == 152
2327 end
2328 end
2329
2330 describe "change_email/2" do
2331 setup do
2332 [user: insert(:user)]
2333 end
2334
2335 test "blank email returns error if we require an email on registration", %{user: user} do
2336 orig_account_activation_required =
2337 Pleroma.Config.get([:instance, :account_activation_required])
2338
2339 Pleroma.Config.put([:instance, :account_activation_required], true)
2340
2341 on_exit(fn ->
2342 Pleroma.Config.put(
2343 [:instance, :account_activation_required],
2344 orig_account_activation_required
2345 )
2346 end)
2347
2348 assert {:error, %{errors: [email: {"can't be blank", _}]}} = User.change_email(user, "")
2349 assert {:error, %{errors: [email: {"can't be blank", _}]}} = User.change_email(user, nil)
2350 end
2351
2352 test "blank email should be fine if we do not require an email on registration", %{user: user} do
2353 orig_account_activation_required =
2354 Pleroma.Config.get([:instance, :account_activation_required])
2355
2356 Pleroma.Config.put([:instance, :account_activation_required], false)
2357
2358 on_exit(fn ->
2359 Pleroma.Config.put(
2360 [:instance, :account_activation_required],
2361 orig_account_activation_required
2362 )
2363 end)
2364
2365 assert {:ok, %User{email: nil}} = User.change_email(user, "")
2366 assert {:ok, %User{email: nil}} = User.change_email(user, nil)
2367 end
2368
2369 test "non unique email returns error", %{user: user} do
2370 %{email: email} = insert(:user)
2371
2372 assert {:error, %{errors: [email: {"has already been taken", _}]}} =
2373 User.change_email(user, email)
2374 end
2375
2376 test "invalid email returns error", %{user: user} do
2377 assert {:error, %{errors: [email: {"has invalid format", _}]}} =
2378 User.change_email(user, "cofe")
2379 end
2380
2381 test "changes email", %{user: user} do
2382 assert {:ok, %User{email: "cofe@cofe.party"}} = User.change_email(user, "cofe@cofe.party")
2383 end
2384
2385 test "adds email", %{user: user} do
2386 orig_account_activation_required =
2387 Pleroma.Config.get([:instance, :account_activation_required])
2388
2389 Pleroma.Config.put([:instance, :account_activation_required], false)
2390
2391 on_exit(fn ->
2392 Pleroma.Config.put(
2393 [:instance, :account_activation_required],
2394 orig_account_activation_required
2395 )
2396 end)
2397
2398 assert {:ok, _} = User.change_email(user, "")
2399 Pleroma.Config.put([:instance, :account_activation_required], true)
2400
2401 assert {:ok, %User{email: "cofe2@cofe.party"}} = User.change_email(user, "cofe2@cofe.party")
2402 end
2403 end
2404
2405 describe "get_cached_by_nickname_or_id" do
2406 setup do
2407 local_user = insert(:user)
2408 remote_user = insert(:user, nickname: "nickname@example.com", local: false)
2409
2410 [local_user: local_user, remote_user: remote_user]
2411 end
2412
2413 setup do: clear_config([:instance, :limit_to_local_content])
2414
2415 test "allows getting remote users by id no matter what :limit_to_local_content is set to", %{
2416 remote_user: remote_user
2417 } do
2418 clear_config([:instance, :limit_to_local_content], false)
2419 assert %User{} = User.get_cached_by_nickname_or_id(remote_user.id)
2420
2421 clear_config([:instance, :limit_to_local_content], true)
2422 assert %User{} = User.get_cached_by_nickname_or_id(remote_user.id)
2423
2424 clear_config([:instance, :limit_to_local_content], :unauthenticated)
2425 assert %User{} = User.get_cached_by_nickname_or_id(remote_user.id)
2426 end
2427
2428 test "disallows getting remote users by nickname without authentication when :limit_to_local_content is set to :unauthenticated",
2429 %{remote_user: remote_user} do
2430 clear_config([:instance, :limit_to_local_content], :unauthenticated)
2431 assert nil == User.get_cached_by_nickname_or_id(remote_user.nickname)
2432 end
2433
2434 test "allows getting remote users by nickname with authentication when :limit_to_local_content is set to :unauthenticated",
2435 %{remote_user: remote_user, local_user: local_user} do
2436 clear_config([:instance, :limit_to_local_content], :unauthenticated)
2437 assert %User{} = User.get_cached_by_nickname_or_id(remote_user.nickname, for: local_user)
2438 end
2439
2440 test "disallows getting remote users by nickname when :limit_to_local_content is set to true",
2441 %{remote_user: remote_user} do
2442 clear_config([:instance, :limit_to_local_content], true)
2443 assert nil == User.get_cached_by_nickname_or_id(remote_user.nickname)
2444 end
2445
2446 test "allows getting local users by nickname no matter what :limit_to_local_content is set to",
2447 %{local_user: local_user} do
2448 clear_config([:instance, :limit_to_local_content], false)
2449 assert %User{} = User.get_cached_by_nickname_or_id(local_user.nickname)
2450
2451 clear_config([:instance, :limit_to_local_content], true)
2452 assert %User{} = User.get_cached_by_nickname_or_id(local_user.nickname)
2453
2454 clear_config([:instance, :limit_to_local_content], :unauthenticated)
2455 assert %User{} = User.get_cached_by_nickname_or_id(local_user.nickname)
2456 end
2457 end
2458
2459 describe "update_email_notifications/2" do
2460 setup do
2461 user = insert(:user, email_notifications: %{"digest" => true})
2462
2463 {:ok, user: user}
2464 end
2465
2466 test "Notifications are updated", %{user: user} do
2467 true = user.email_notifications["digest"]
2468 assert {:ok, result} = User.update_email_notifications(user, %{"digest" => false})
2469 assert result.email_notifications["digest"] == false
2470 end
2471 end
2472
2473 describe "local_nickname/1" do
2474 test "returns nickname without host" do
2475 assert User.local_nickname("@mentioned") == "mentioned"
2476 assert User.local_nickname("a_local_nickname") == "a_local_nickname"
2477 assert User.local_nickname("nickname@host.com") == "nickname"
2478 end
2479 end
2480
2481 describe "full_nickname/1" do
2482 test "returns fully qualified nickname for local and remote users" do
2483 local_user =
2484 insert(:user, nickname: "local_user", ap_id: "https://somehost.com/users/local_user")
2485
2486 remote_user = insert(:user, nickname: "remote@host.com", local: false)
2487
2488 assert User.full_nickname(local_user) == "local_user@somehost.com"
2489 assert User.full_nickname(remote_user) == "remote@host.com"
2490 end
2491
2492 test "strips leading @ from mentions" do
2493 assert User.full_nickname("@mentioned") == "mentioned"
2494 assert User.full_nickname("@nickname@host.com") == "nickname@host.com"
2495 end
2496
2497 test "does not modify nicknames" do
2498 assert User.full_nickname("nickname") == "nickname"
2499 assert User.full_nickname("nickname@host.com") == "nickname@host.com"
2500 end
2501 end
2502
2503 test "avatar fallback" do
2504 user = insert(:user)
2505 assert User.avatar_url(user) =~ "/images/avi.png"
2506
2507 clear_config([:assets, :default_user_avatar], "avatar.png")
2508
2509 user = User.get_cached_by_nickname_or_id(user.nickname)
2510 assert User.avatar_url(user) =~ "avatar.png"
2511
2512 assert User.avatar_url(user, no_default: true) == nil
2513 end
2514
2515 test "get_host/1" do
2516 user = insert(:user, ap_id: "https://lain.com/users/lain", nickname: "lain")
2517 assert User.get_host(user) == "lain.com"
2518 end
2519
2520 test "update_last_active_at/1" do
2521 user = insert(:user)
2522 assert is_nil(user.last_active_at)
2523
2524 test_started_at = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
2525
2526 assert {:ok, user} = User.update_last_active_at(user)
2527
2528 assert user.last_active_at >= test_started_at
2529 assert user.last_active_at <= NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
2530
2531 last_active_at =
2532 NaiveDateTime.utc_now()
2533 |> NaiveDateTime.add(-:timer.hours(24), :millisecond)
2534 |> NaiveDateTime.truncate(:second)
2535
2536 assert {:ok, user} =
2537 user
2538 |> cast(%{last_active_at: last_active_at}, [:last_active_at])
2539 |> User.update_and_set_cache()
2540
2541 assert user.last_active_at == last_active_at
2542 assert {:ok, user} = User.update_last_active_at(user)
2543 assert user.last_active_at >= test_started_at
2544 assert user.last_active_at <= NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
2545 end
2546
2547 test "active_user_count/1" do
2548 insert(:user)
2549 insert(:user, %{local: false})
2550 insert(:user, %{last_active_at: NaiveDateTime.utc_now()})
2551 insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), days: -15)})
2552 insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), weeks: -6)})
2553 insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), months: -7)})
2554 insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), years: -2)})
2555
2556 assert User.active_user_count() == 2
2557 assert User.active_user_count(180) == 3
2558 assert User.active_user_count(365) == 4
2559 assert User.active_user_count(1000) == 5
2560 end
2561
2562 describe "pins" do
2563 setup do
2564 user = insert(:user)
2565
2566 [user: user, object_id: object_id_from_created_activity(user)]
2567 end
2568
2569 test "unique pins", %{user: user, object_id: object_id} do
2570 assert {:ok, %{pinned_objects: %{^object_id => pinned_at1} = pins} = updated_user} =
2571 User.add_pinned_object_id(user, object_id)
2572
2573 assert Enum.count(pins) == 1
2574
2575 assert {:ok, %{pinned_objects: %{^object_id => pinned_at2} = pins}} =
2576 User.add_pinned_object_id(updated_user, object_id)
2577
2578 assert pinned_at1 == pinned_at2
2579
2580 assert Enum.count(pins) == 1
2581 end
2582
2583 test "respects max_pinned_statuses limit", %{user: user, object_id: object_id} do
2584 clear_config([:instance, :max_pinned_statuses], 1)
2585 {:ok, updated} = User.add_pinned_object_id(user, object_id)
2586
2587 object_id2 = object_id_from_created_activity(user)
2588
2589 {:error, %{errors: errors}} = User.add_pinned_object_id(updated, object_id2)
2590 assert Keyword.has_key?(errors, :pinned_objects)
2591 end
2592
2593 test "remove_pinned_object_id/2", %{user: user, object_id: object_id} do
2594 assert {:ok, updated} = User.add_pinned_object_id(user, object_id)
2595
2596 {:ok, after_remove} = User.remove_pinned_object_id(updated, object_id)
2597 assert after_remove.pinned_objects == %{}
2598 end
2599 end
2600
2601 defp object_id_from_created_activity(user) do
2602 %{id: id} = insert(:note_activity, user: user)
2603 %{object: %{data: %{"id" => object_id}}} = Activity.get_by_id_with_object(id)
2604 object_id
2605 end
2606
2607 describe "add_alias/2" do
2608 test "should add alias for another user" do
2609 user = insert(:user)
2610 user2 = insert(:user)
2611
2612 assert {:ok, user_updated} = user |> User.add_alias(user2)
2613
2614 assert user_updated.also_known_as |> length() == 1
2615 assert user2.ap_id in user_updated.also_known_as
2616 end
2617
2618 test "should add multiple aliases" do
2619 user = insert(:user)
2620 user2 = insert(:user)
2621 user3 = insert(:user)
2622
2623 assert {:ok, user} = user |> User.add_alias(user2)
2624 assert {:ok, user_updated} = user |> User.add_alias(user3)
2625
2626 assert user_updated.also_known_as |> length() == 2
2627 assert user2.ap_id in user_updated.also_known_as
2628 assert user3.ap_id in user_updated.also_known_as
2629 end
2630
2631 test "should not add duplicate aliases" do
2632 user = insert(:user)
2633 user2 = insert(:user)
2634
2635 assert {:ok, user} = user |> User.add_alias(user2)
2636
2637 assert {:ok, user_updated} = user |> User.add_alias(user2)
2638
2639 assert user_updated.also_known_as |> length() == 1
2640 assert user2.ap_id in user_updated.also_known_as
2641 end
2642 end
2643
2644 describe "alias_users/1" do
2645 test "should get aliases for a user" do
2646 user = insert(:user)
2647 user2 = insert(:user, also_known_as: [user.ap_id])
2648
2649 aliases = user2 |> User.alias_users()
2650
2651 assert aliases |> length() == 1
2652
2653 alias_user = aliases |> Enum.at(0)
2654
2655 assert alias_user.ap_id == user.ap_id
2656 end
2657 end
2658
2659 describe "delete_alias/2" do
2660 test "should delete existing alias" do
2661 user = insert(:user)
2662 user2 = insert(:user, also_known_as: [user.ap_id])
2663
2664 assert {:ok, user_updated} = user2 |> User.delete_alias(user)
2665
2666 assert user_updated.also_known_as == []
2667 end
2668
2669 test "should report error on non-existing alias" do
2670 user = insert(:user)
2671 user2 = insert(:user)
2672 user3 = insert(:user, also_known_as: [user.ap_id])
2673
2674 assert {:error, :no_such_alias} = user3 |> User.delete_alias(user2)
2675
2676 user3_updated = User.get_cached_by_ap_id(user3.ap_id)
2677
2678 assert user3_updated.also_known_as |> length() == 1
2679 assert user.ap_id in user3_updated.also_known_as
2680 end
2681 end
2682 end