a590946c215ea2d3781b75b76ec1028a50945923
[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 |> assert_email_sent()
503 end
504
505 test "sends a pending approval email" do
506 clear_config([:instance, :account_approval_required], true)
507
508 {:ok, user} =
509 User.register_changeset(%User{}, @full_user_data)
510 |> User.register()
511
512 ObanHelpers.perform_all()
513
514 assert_email_sent(
515 from: Pleroma.Config.Helpers.sender(),
516 to: {user.name, user.email},
517 subject: "Your account is awaiting approval"
518 )
519 end
520
521 test "it sends a registration confirmed email if no others will be sent" do
522 clear_config([:welcome, :email, :enabled], false)
523 clear_config([:instance, :account_activation_required], false)
524 clear_config([:instance, :account_approval_required], false)
525
526 {:ok, user} =
527 User.register_changeset(%User{}, @full_user_data)
528 |> User.register()
529
530 ObanHelpers.perform_all()
531
532 instance_name = Pleroma.Config.get([:instance, :name])
533 sender = Pleroma.Config.get([:instance, :notify_email])
534
535 assert_email_sent(
536 from: {instance_name, sender},
537 to: {user.name, user.email},
538 subject: "Account registered on #{instance_name}"
539 )
540 end
541
542 test "it fails gracefully with invalid email config" do
543 cng = User.register_changeset(%User{}, @full_user_data)
544
545 # Disable the mailer but enable all the things that want to send emails
546 clear_config([Pleroma.Emails.Mailer, :enabled], false)
547 clear_config([:instance, :account_activation_required], true)
548 clear_config([:instance, :account_approval_required], true)
549 clear_config([:welcome, :email, :enabled], true)
550 clear_config([:welcome, :email, :sender], "lain@lain.com")
551
552 # The user is still created
553 assert {:ok, %User{nickname: "nick"}} = User.register(cng)
554
555 # No emails are sent
556 ObanHelpers.perform_all()
557 refute_email_sent()
558 end
559
560 test "it requires an email, name, nickname and password, bio is optional when account_activation_required is enabled" do
561 clear_config([:instance, :account_activation_required], true)
562
563 @full_user_data
564 |> Map.keys()
565 |> Enum.each(fn key ->
566 params = Map.delete(@full_user_data, key)
567 changeset = User.register_changeset(%User{}, params)
568
569 assert if key == :bio, do: changeset.valid?, else: not changeset.valid?
570 end)
571 end
572
573 test "it requires an name, nickname and password, bio and email are optional when account_activation_required is disabled" do
574 clear_config([:instance, :account_activation_required], false)
575
576 @full_user_data
577 |> Map.keys()
578 |> Enum.each(fn key ->
579 params = Map.delete(@full_user_data, key)
580 changeset = User.register_changeset(%User{}, params)
581
582 assert if key in [:bio, :email], do: changeset.valid?, else: not changeset.valid?
583 end)
584 end
585
586 test "it restricts certain nicknames" do
587 [restricted_name | _] = Pleroma.Config.get([User, :restricted_nicknames])
588
589 assert is_bitstring(restricted_name)
590
591 params =
592 @full_user_data
593 |> Map.put(:nickname, restricted_name)
594
595 changeset = User.register_changeset(%User{}, params)
596
597 refute changeset.valid?
598 end
599
600 test "it blocks blacklisted email domains" do
601 clear_config([User, :email_blacklist], ["trolling.world"])
602
603 # Block with match
604 params = Map.put(@full_user_data, :email, "troll@trolling.world")
605 changeset = User.register_changeset(%User{}, params)
606 refute changeset.valid?
607
608 # Block with subdomain match
609 params = Map.put(@full_user_data, :email, "troll@gnomes.trolling.world")
610 changeset = User.register_changeset(%User{}, params)
611 refute changeset.valid?
612
613 # Pass with different domains that are similar
614 params = Map.put(@full_user_data, :email, "troll@gnomestrolling.world")
615 changeset = User.register_changeset(%User{}, params)
616 assert changeset.valid?
617
618 params = Map.put(@full_user_data, :email, "troll@trolling.world.us")
619 changeset = User.register_changeset(%User{}, params)
620 assert changeset.valid?
621 end
622
623 test "it sets the password_hash, ap_id, private key and followers collection address" do
624 changeset = User.register_changeset(%User{}, @full_user_data)
625
626 assert changeset.valid?
627
628 assert is_binary(changeset.changes[:password_hash])
629 assert is_binary(changeset.changes[:keys])
630 assert changeset.changes[:ap_id] == User.ap_id(%User{nickname: @full_user_data.nickname})
631 assert is_binary(changeset.changes[:keys])
632 assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers"
633 end
634
635 test "it creates a confirmed user" do
636 changeset = User.register_changeset(%User{}, @full_user_data)
637 assert changeset.valid?
638
639 {:ok, user} = Repo.insert(changeset)
640
641 assert user.is_confirmed
642 end
643 end
644
645 describe "user registration, with :account_activation_required" do
646 @full_user_data %{
647 bio: "A guy",
648 name: "my name",
649 nickname: "nick",
650 password: "test",
651 password_confirmation: "test",
652 email: "email@example.com"
653 }
654 setup do: clear_config([:instance, :account_activation_required], true)
655
656 test "it creates unconfirmed user" do
657 changeset = User.register_changeset(%User{}, @full_user_data)
658 assert changeset.valid?
659
660 {:ok, user} = Repo.insert(changeset)
661
662 refute user.is_confirmed
663 assert user.confirmation_token
664 end
665
666 test "it creates confirmed user if :confirmed option is given" do
667 changeset = User.register_changeset(%User{}, @full_user_data, confirmed: true)
668 assert changeset.valid?
669
670 {:ok, user} = Repo.insert(changeset)
671
672 assert user.is_confirmed
673 refute user.confirmation_token
674 end
675 end
676
677 describe "user registration, with :account_approval_required" do
678 @full_user_data %{
679 bio: "A guy",
680 name: "my name",
681 nickname: "nick",
682 password: "test",
683 password_confirmation: "test",
684 email: "email@example.com",
685 registration_reason: "I'm a cool guy :)"
686 }
687 setup do: clear_config([:instance, :account_approval_required], true)
688
689 test "it creates unapproved user" do
690 changeset = User.register_changeset(%User{}, @full_user_data)
691 assert changeset.valid?
692
693 {:ok, user} = Repo.insert(changeset)
694
695 refute user.is_approved
696 assert user.registration_reason == "I'm a cool guy :)"
697 end
698
699 test "it restricts length of registration reason" do
700 reason_limit = Pleroma.Config.get([:instance, :registration_reason_length])
701
702 assert is_integer(reason_limit)
703
704 params =
705 @full_user_data
706 |> Map.put(
707 :registration_reason,
708 "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."
709 )
710
711 changeset = User.register_changeset(%User{}, params)
712
713 refute changeset.valid?
714 end
715 end
716
717 describe "get_or_fetch/1" do
718 test "gets an existing user by nickname" do
719 user = insert(:user)
720 {:ok, fetched_user} = User.get_or_fetch(user.nickname)
721
722 assert user == fetched_user
723 end
724
725 test "gets an existing user by ap_id" do
726 ap_id = "http://mastodon.example.org/users/admin"
727
728 user =
729 insert(
730 :user,
731 local: false,
732 nickname: "admin@mastodon.example.org",
733 ap_id: ap_id
734 )
735
736 {:ok, fetched_user} = User.get_or_fetch(ap_id)
737 freshed_user = refresh_record(user)
738 assert freshed_user == fetched_user
739 end
740
741 test "gets an existing user by nickname starting with http" do
742 user = insert(:user, nickname: "httpssome")
743 {:ok, fetched_user} = User.get_or_fetch("httpssome")
744
745 assert user == fetched_user
746 end
747 end
748
749 describe "get_or_fetch/1 remote users with tld, while BE is runned on subdomain" do
750 setup do: clear_config([Pleroma.Web.WebFinger, :update_nickname_on_user_fetch], true)
751
752 test "for mastodon" do
753 Tesla.Mock.mock(fn
754 %{url: "https://example.com/.well-known/host-meta"} ->
755 %Tesla.Env{
756 status: 302,
757 headers: [{"location", "https://sub.example.com/.well-known/host-meta"}]
758 }
759
760 %{url: "https://sub.example.com/.well-known/host-meta"} ->
761 %Tesla.Env{
762 status: 200,
763 body:
764 "test/fixtures/webfinger/masto-host-meta.xml"
765 |> File.read!()
766 |> String.replace("{{domain}}", "sub.example.com")
767 }
768
769 %{url: "https://sub.example.com/.well-known/webfinger?resource=acct:a@example.com"} ->
770 %Tesla.Env{
771 status: 200,
772 body:
773 "test/fixtures/webfinger/masto-webfinger.json"
774 |> File.read!()
775 |> String.replace("{{nickname}}", "a")
776 |> String.replace("{{domain}}", "example.com")
777 |> String.replace("{{subdomain}}", "sub.example.com"),
778 headers: [{"content-type", "application/jrd+json"}]
779 }
780
781 %{url: "https://sub.example.com/users/a"} ->
782 %Tesla.Env{
783 status: 200,
784 body:
785 "test/fixtures/webfinger/masto-user.json"
786 |> File.read!()
787 |> String.replace("{{nickname}}", "a")
788 |> String.replace("{{domain}}", "sub.example.com"),
789 headers: [{"content-type", "application/activity+json"}]
790 }
791
792 %{url: "https://sub.example.com/users/a/collections/featured"} ->
793 %Tesla.Env{
794 status: 200,
795 body:
796 File.read!("test/fixtures/users_mock/masto_featured.json")
797 |> String.replace("{{domain}}", "sub.example.com")
798 |> String.replace("{{nickname}}", "a"),
799 headers: [{"content-type", "application/activity+json"}]
800 }
801 end)
802
803 ap_id = "a@example.com"
804 {:ok, fetched_user} = User.get_or_fetch(ap_id)
805
806 assert fetched_user.ap_id == "https://sub.example.com/users/a"
807 assert fetched_user.nickname == "a@example.com"
808 end
809
810 test "for pleroma" do
811 Tesla.Mock.mock(fn
812 %{url: "https://example.com/.well-known/host-meta"} ->
813 %Tesla.Env{
814 status: 302,
815 headers: [{"location", "https://sub.example.com/.well-known/host-meta"}]
816 }
817
818 %{url: "https://sub.example.com/.well-known/host-meta"} ->
819 %Tesla.Env{
820 status: 200,
821 body:
822 "test/fixtures/webfinger/pleroma-host-meta.xml"
823 |> File.read!()
824 |> String.replace("{{domain}}", "sub.example.com")
825 }
826
827 %{url: "https://sub.example.com/.well-known/webfinger?resource=acct:a@example.com"} ->
828 %Tesla.Env{
829 status: 200,
830 body:
831 "test/fixtures/webfinger/pleroma-webfinger.json"
832 |> File.read!()
833 |> String.replace("{{nickname}}", "a")
834 |> String.replace("{{domain}}", "example.com")
835 |> String.replace("{{subdomain}}", "sub.example.com"),
836 headers: [{"content-type", "application/jrd+json"}]
837 }
838
839 %{url: "https://sub.example.com/users/a"} ->
840 %Tesla.Env{
841 status: 200,
842 body:
843 "test/fixtures/webfinger/pleroma-user.json"
844 |> File.read!()
845 |> String.replace("{{nickname}}", "a")
846 |> String.replace("{{domain}}", "sub.example.com"),
847 headers: [{"content-type", "application/activity+json"}]
848 }
849 end)
850
851 ap_id = "a@example.com"
852 {:ok, fetched_user} = User.get_or_fetch(ap_id)
853
854 assert fetched_user.ap_id == "https://sub.example.com/users/a"
855 assert fetched_user.nickname == "a@example.com"
856 end
857 end
858
859 describe "fetching a user from nickname or trying to build one" do
860 test "gets an existing user" do
861 user = insert(:user)
862 {:ok, fetched_user} = User.get_or_fetch_by_nickname(user.nickname)
863
864 assert user == fetched_user
865 end
866
867 test "gets an existing user, case insensitive" do
868 user = insert(:user, nickname: "nick")
869 {:ok, fetched_user} = User.get_or_fetch_by_nickname("NICK")
870
871 assert user == fetched_user
872 end
873
874 test "gets an existing user by fully qualified nickname" do
875 user = insert(:user)
876
877 {:ok, fetched_user} =
878 User.get_or_fetch_by_nickname(user.nickname <> "@" <> Pleroma.Web.Endpoint.host())
879
880 assert user == fetched_user
881 end
882
883 test "gets an existing user by fully qualified nickname, case insensitive" do
884 user = insert(:user, nickname: "nick")
885 casing_altered_fqn = String.upcase(user.nickname <> "@" <> Pleroma.Web.Endpoint.host())
886
887 {:ok, fetched_user} = User.get_or_fetch_by_nickname(casing_altered_fqn)
888
889 assert user == fetched_user
890 end
891
892 @tag capture_log: true
893 test "returns nil if no user could be fetched" do
894 {:error, fetched_user} = User.get_or_fetch_by_nickname("nonexistant@social.heldscal.la")
895 assert fetched_user == "not found nonexistant@social.heldscal.la"
896 end
897
898 test "returns nil for nonexistant local user" do
899 {:error, fetched_user} = User.get_or_fetch_by_nickname("nonexistant")
900 assert fetched_user == "not found nonexistant"
901 end
902
903 test "updates an existing user, if stale" do
904 a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800)
905
906 orig_user =
907 insert(
908 :user,
909 local: false,
910 nickname: "admin@mastodon.example.org",
911 ap_id: "http://mastodon.example.org/users/admin",
912 last_refreshed_at: a_week_ago
913 )
914
915 assert orig_user.last_refreshed_at == a_week_ago
916
917 {:ok, user} = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/admin")
918
919 assert user.inbox
920
921 refute user.last_refreshed_at == orig_user.last_refreshed_at
922 end
923
924 test "if nicknames clash, the old user gets a prefix with the old id to the nickname" do
925 a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800)
926
927 orig_user =
928 insert(
929 :user,
930 local: false,
931 nickname: "admin@mastodon.example.org",
932 ap_id: "http://mastodon.example.org/users/harinezumigari",
933 last_refreshed_at: a_week_ago
934 )
935
936 assert orig_user.last_refreshed_at == a_week_ago
937
938 {:ok, user} = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/admin")
939
940 assert user.inbox
941
942 refute user.id == orig_user.id
943
944 orig_user = User.get_by_id(orig_user.id)
945
946 assert orig_user.nickname == "#{orig_user.id}.admin@mastodon.example.org"
947 end
948
949 @tag capture_log: true
950 test "it returns the old user if stale, but unfetchable" do
951 a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800)
952
953 orig_user =
954 insert(
955 :user,
956 local: false,
957 nickname: "admin@mastodon.example.org",
958 ap_id: "http://mastodon.example.org/users/raymoo",
959 last_refreshed_at: a_week_ago
960 )
961
962 assert orig_user.last_refreshed_at == a_week_ago
963
964 {:ok, user} = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/raymoo")
965
966 assert user.last_refreshed_at == orig_user.last_refreshed_at
967 end
968
969 test "it doesn't fail on invalid alsoKnownAs entries" do
970 Tesla.Mock.mock(fn
971 %{url: "https://mbp.example.com/"} ->
972 %Tesla.Env{
973 status: 200,
974 body:
975 "test/fixtures/microblogpub/user_with_invalid_also_known_as.json"
976 |> File.read!(),
977 headers: [{"content-type", "application/activity+json"}]
978 }
979
980 _ ->
981 %Tesla.Env{status: 404}
982 end)
983
984 assert {:ok, %User{also_known_as: []}} =
985 User.get_or_fetch_by_ap_id("https://mbp.example.com/")
986 end
987 end
988
989 test "returns an ap_id for a user" do
990 user = insert(:user)
991
992 assert User.ap_id(user) ==
993 Pleroma.Web.Router.Helpers.user_feed_url(
994 Pleroma.Web.Endpoint,
995 :feed_redirect,
996 user.nickname
997 )
998 end
999
1000 test "returns an ap_followers link for a user" do
1001 user = insert(:user)
1002
1003 assert User.ap_followers(user) ==
1004 Pleroma.Web.Router.Helpers.user_feed_url(
1005 Pleroma.Web.Endpoint,
1006 :feed_redirect,
1007 user.nickname
1008 ) <> "/followers"
1009 end
1010
1011 describe "remote user changeset" do
1012 @valid_remote %{
1013 bio: "hello",
1014 name: "Someone",
1015 nickname: "a@b.de",
1016 ap_id: "http...",
1017 avatar: %{some: "avatar"}
1018 }
1019 setup do: clear_config([:instance, :user_bio_length])
1020 setup do: clear_config([:instance, :user_name_length])
1021
1022 test "it confirms validity" do
1023 cs = User.remote_user_changeset(@valid_remote)
1024 assert cs.valid?
1025 end
1026
1027 test "it sets the follower_adress" do
1028 cs = User.remote_user_changeset(@valid_remote)
1029 # remote users get a fake local follower address
1030 assert cs.changes.follower_address ==
1031 User.ap_followers(%User{nickname: @valid_remote[:nickname]})
1032 end
1033
1034 test "it enforces the fqn format for nicknames" do
1035 cs = User.remote_user_changeset(%{@valid_remote | nickname: "bla"})
1036 assert Ecto.Changeset.get_field(cs, :local) == false
1037 assert cs.changes.avatar
1038 refute cs.valid?
1039 end
1040
1041 test "it has required fields" do
1042 [:ap_id]
1043 |> Enum.each(fn field ->
1044 cs = User.remote_user_changeset(Map.delete(@valid_remote, field))
1045 refute cs.valid?
1046 end)
1047 end
1048
1049 test "it is invalid given a local user" do
1050 user = insert(:user)
1051 cs = User.remote_user_changeset(user, %{name: "tom from myspace"})
1052
1053 refute cs.valid?
1054 end
1055 end
1056
1057 describe "followers and friends" do
1058 test "gets all followers for a given user" do
1059 user = insert(:user)
1060 follower_one = insert(:user)
1061 follower_two = insert(:user)
1062 not_follower = insert(:user)
1063
1064 {:ok, follower_one, user} = User.follow(follower_one, user)
1065 {:ok, follower_two, user} = User.follow(follower_two, user)
1066
1067 res = User.get_followers(user)
1068
1069 assert Enum.member?(res, follower_one)
1070 assert Enum.member?(res, follower_two)
1071 refute Enum.member?(res, not_follower)
1072 end
1073
1074 test "gets all friends (followed users) for a given user" do
1075 user = insert(:user)
1076 followed_one = insert(:user)
1077 followed_two = insert(:user)
1078 not_followed = insert(:user)
1079
1080 {:ok, user, followed_one} = User.follow(user, followed_one)
1081 {:ok, user, followed_two} = User.follow(user, followed_two)
1082
1083 res = User.get_friends(user)
1084
1085 followed_one = User.get_cached_by_ap_id(followed_one.ap_id)
1086 followed_two = User.get_cached_by_ap_id(followed_two.ap_id)
1087 assert Enum.member?(res, followed_one)
1088 assert Enum.member?(res, followed_two)
1089 refute Enum.member?(res, not_followed)
1090 end
1091 end
1092
1093 describe "updating note and follower count" do
1094 test "it sets the note_count property" do
1095 note = insert(:note)
1096
1097 user = User.get_cached_by_ap_id(note.data["actor"])
1098
1099 assert user.note_count == 0
1100
1101 {:ok, user} = User.update_note_count(user)
1102
1103 assert user.note_count == 1
1104 end
1105
1106 test "it increases the note_count property" do
1107 note = insert(:note)
1108 user = User.get_cached_by_ap_id(note.data["actor"])
1109
1110 assert user.note_count == 0
1111
1112 {:ok, user} = User.increase_note_count(user)
1113
1114 assert user.note_count == 1
1115
1116 {:ok, user} = User.increase_note_count(user)
1117
1118 assert user.note_count == 2
1119 end
1120
1121 test "it decreases the note_count property" do
1122 note = insert(:note)
1123 user = User.get_cached_by_ap_id(note.data["actor"])
1124
1125 assert user.note_count == 0
1126
1127 {:ok, user} = User.increase_note_count(user)
1128
1129 assert user.note_count == 1
1130
1131 {:ok, user} = User.decrease_note_count(user)
1132
1133 assert user.note_count == 0
1134
1135 {:ok, user} = User.decrease_note_count(user)
1136
1137 assert user.note_count == 0
1138 end
1139
1140 test "it sets the follower_count property" do
1141 user = insert(:user)
1142 follower = insert(:user)
1143
1144 User.follow(follower, user)
1145
1146 assert user.follower_count == 0
1147
1148 {:ok, user} = User.update_follower_count(user)
1149
1150 assert user.follower_count == 1
1151 end
1152 end
1153
1154 describe "mutes" do
1155 test "it mutes people" do
1156 user = insert(:user)
1157 muted_user = insert(:user)
1158
1159 refute User.mutes?(user, muted_user)
1160 refute User.muted_notifications?(user, muted_user)
1161
1162 {:ok, _user_relationships} = User.mute(user, muted_user)
1163
1164 assert User.mutes?(user, muted_user)
1165 assert User.muted_notifications?(user, muted_user)
1166 end
1167
1168 test "expiring" do
1169 user = insert(:user)
1170 muted_user = insert(:user)
1171
1172 {:ok, _user_relationships} = User.mute(user, muted_user, %{expires_in: 60})
1173 assert User.mutes?(user, muted_user)
1174
1175 worker = Pleroma.Workers.MuteExpireWorker
1176 args = %{"op" => "unmute_user", "muter_id" => user.id, "mutee_id" => muted_user.id}
1177
1178 assert_enqueued(
1179 worker: worker,
1180 args: args
1181 )
1182
1183 assert :ok = perform_job(worker, args)
1184
1185 refute User.mutes?(user, muted_user)
1186 refute User.muted_notifications?(user, muted_user)
1187 end
1188
1189 test "it unmutes users" do
1190 user = insert(:user)
1191 muted_user = insert(:user)
1192
1193 {:ok, _user_relationships} = User.mute(user, muted_user)
1194 {:ok, _user_mute} = User.unmute(user, muted_user)
1195
1196 refute User.mutes?(user, muted_user)
1197 refute User.muted_notifications?(user, muted_user)
1198 end
1199
1200 test "it unmutes users by id" do
1201 user = insert(:user)
1202 muted_user = insert(:user)
1203
1204 {:ok, _user_relationships} = User.mute(user, muted_user)
1205 {:ok, _user_mute} = User.unmute(user.id, muted_user.id)
1206
1207 refute User.mutes?(user, muted_user)
1208 refute User.muted_notifications?(user, muted_user)
1209 end
1210
1211 test "it mutes user without notifications" do
1212 user = insert(:user)
1213 muted_user = insert(:user)
1214
1215 refute User.mutes?(user, muted_user)
1216 refute User.muted_notifications?(user, muted_user)
1217
1218 {:ok, _user_relationships} = User.mute(user, muted_user, %{notifications: false})
1219
1220 assert User.mutes?(user, muted_user)
1221 refute User.muted_notifications?(user, muted_user)
1222 end
1223 end
1224
1225 describe "blocks" do
1226 test "it blocks people" do
1227 user = insert(:user)
1228 blocked_user = insert(:user)
1229
1230 refute User.blocks?(user, blocked_user)
1231
1232 {:ok, _user_relationship} = User.block(user, blocked_user)
1233
1234 assert User.blocks?(user, blocked_user)
1235 end
1236
1237 test "it unblocks users" do
1238 user = insert(:user)
1239 blocked_user = insert(:user)
1240
1241 {:ok, _user_relationship} = User.block(user, blocked_user)
1242 {:ok, _user_block} = User.unblock(user, blocked_user)
1243
1244 refute User.blocks?(user, blocked_user)
1245 end
1246
1247 test "blocks tear down cyclical follow relationships" do
1248 blocker = insert(:user)
1249 blocked = insert(:user)
1250
1251 {:ok, blocker, blocked} = User.follow(blocker, blocked)
1252 {:ok, blocked, blocker} = User.follow(blocked, blocker)
1253
1254 assert User.following?(blocker, blocked)
1255 assert User.following?(blocked, blocker)
1256
1257 {:ok, _user_relationship} = User.block(blocker, blocked)
1258 blocked = User.get_cached_by_id(blocked.id)
1259
1260 assert User.blocks?(blocker, blocked)
1261
1262 refute User.following?(blocker, blocked)
1263 refute User.following?(blocked, blocker)
1264 end
1265
1266 test "blocks tear down blocker->blocked follow relationships" do
1267 blocker = insert(:user)
1268 blocked = insert(:user)
1269
1270 {:ok, blocker, blocked} = User.follow(blocker, blocked)
1271
1272 assert User.following?(blocker, blocked)
1273 refute User.following?(blocked, blocker)
1274
1275 {:ok, _user_relationship} = User.block(blocker, blocked)
1276 blocked = User.get_cached_by_id(blocked.id)
1277
1278 assert User.blocks?(blocker, blocked)
1279
1280 refute User.following?(blocker, blocked)
1281 refute User.following?(blocked, blocker)
1282 end
1283
1284 test "blocks tear down blocked->blocker follow relationships" do
1285 blocker = insert(:user)
1286 blocked = insert(:user)
1287
1288 {:ok, blocked, blocker} = User.follow(blocked, blocker)
1289
1290 refute User.following?(blocker, blocked)
1291 assert User.following?(blocked, blocker)
1292
1293 {:ok, _user_relationship} = User.block(blocker, blocked)
1294 blocked = User.get_cached_by_id(blocked.id)
1295
1296 assert User.blocks?(blocker, blocked)
1297
1298 refute User.following?(blocker, blocked)
1299 refute User.following?(blocked, blocker)
1300 end
1301
1302 test "blocks tear down blocked->blocker subscription relationships" do
1303 blocker = insert(:user)
1304 blocked = insert(:user)
1305
1306 {:ok, _subscription} = User.subscribe(blocked, blocker)
1307
1308 assert User.subscribed_to?(blocked, blocker)
1309 refute User.subscribed_to?(blocker, blocked)
1310
1311 {:ok, _user_relationship} = User.block(blocker, blocked)
1312
1313 assert User.blocks?(blocker, blocked)
1314 refute User.subscribed_to?(blocker, blocked)
1315 refute User.subscribed_to?(blocked, blocker)
1316 end
1317 end
1318
1319 describe "domain blocking" do
1320 test "blocks domains" do
1321 user = insert(:user)
1322 collateral_user = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
1323
1324 {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
1325
1326 assert User.blocks?(user, collateral_user)
1327 end
1328
1329 test "does not block domain with same end" do
1330 user = insert(:user)
1331
1332 collateral_user =
1333 insert(:user, %{ap_id: "https://another-awful-and-rude-instance.com/user/bully"})
1334
1335 {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
1336
1337 refute User.blocks?(user, collateral_user)
1338 end
1339
1340 test "does not block domain with same end if wildcard added" do
1341 user = insert(:user)
1342
1343 collateral_user =
1344 insert(:user, %{ap_id: "https://another-awful-and-rude-instance.com/user/bully"})
1345
1346 {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
1347
1348 refute User.blocks?(user, collateral_user)
1349 end
1350
1351 test "blocks domain with wildcard for subdomain" do
1352 user = insert(:user)
1353
1354 user_from_subdomain =
1355 insert(:user, %{ap_id: "https://subdomain.awful-and-rude-instance.com/user/bully"})
1356
1357 user_with_two_subdomains =
1358 insert(:user, %{
1359 ap_id: "https://subdomain.second_subdomain.awful-and-rude-instance.com/user/bully"
1360 })
1361
1362 user_domain = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
1363
1364 {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
1365
1366 assert User.blocks?(user, user_from_subdomain)
1367 assert User.blocks?(user, user_with_two_subdomains)
1368 assert User.blocks?(user, user_domain)
1369 end
1370
1371 test "unblocks domains" do
1372 user = insert(:user)
1373 collateral_user = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
1374
1375 {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
1376 {:ok, user} = User.unblock_domain(user, "awful-and-rude-instance.com")
1377
1378 refute User.blocks?(user, collateral_user)
1379 end
1380
1381 test "follows take precedence over domain blocks" do
1382 user = insert(:user)
1383 good_eggo = insert(:user, %{ap_id: "https://meanies.social/user/cuteposter"})
1384
1385 {:ok, user} = User.block_domain(user, "meanies.social")
1386 {:ok, user, good_eggo} = User.follow(user, good_eggo)
1387
1388 refute User.blocks?(user, good_eggo)
1389 end
1390 end
1391
1392 describe "get_recipients_from_activity" do
1393 test "works for announces" do
1394 actor = insert(:user)
1395 user = insert(:user, local: true)
1396
1397 {:ok, activity} = CommonAPI.post(actor, %{status: "hello"})
1398 {:ok, announce} = CommonAPI.repeat(activity.id, user)
1399
1400 recipients = User.get_recipients_from_activity(announce)
1401
1402 assert user in recipients
1403 end
1404
1405 test "get recipients" do
1406 actor = insert(:user)
1407 user = insert(:user, local: true)
1408 user_two = insert(:user, local: false)
1409 addressed = insert(:user, local: true)
1410 addressed_remote = insert(:user, local: false)
1411
1412 {:ok, activity} =
1413 CommonAPI.post(actor, %{
1414 status: "hey @#{addressed.nickname} @#{addressed_remote.nickname}"
1415 })
1416
1417 assert Enum.map([actor, addressed], & &1.ap_id) --
1418 Enum.map(User.get_recipients_from_activity(activity), & &1.ap_id) == []
1419
1420 {:ok, user, actor} = User.follow(user, actor)
1421 {:ok, _user_two, _actor} = User.follow(user_two, actor)
1422 recipients = User.get_recipients_from_activity(activity)
1423 assert length(recipients) == 3
1424 assert user in recipients
1425 assert addressed in recipients
1426 end
1427
1428 test "has following" do
1429 actor = insert(:user)
1430 user = insert(:user)
1431 user_two = insert(:user)
1432 addressed = insert(:user, local: true)
1433
1434 {:ok, activity} =
1435 CommonAPI.post(actor, %{
1436 status: "hey @#{addressed.nickname}"
1437 })
1438
1439 assert Enum.map([actor, addressed], & &1.ap_id) --
1440 Enum.map(User.get_recipients_from_activity(activity), & &1.ap_id) == []
1441
1442 {:ok, _actor, _user} = User.follow(actor, user)
1443 {:ok, _actor, _user_two} = User.follow(actor, user_two)
1444 recipients = User.get_recipients_from_activity(activity)
1445 assert length(recipients) == 2
1446 assert addressed in recipients
1447 end
1448 end
1449
1450 describe ".set_activation" do
1451 test "can de-activate then re-activate a user" do
1452 user = insert(:user)
1453 assert user.is_active
1454 {:ok, user} = User.set_activation(user, false)
1455 refute user.is_active
1456 {:ok, user} = User.set_activation(user, true)
1457 assert user.is_active
1458 end
1459
1460 test "hide a user from followers" do
1461 user = insert(:user)
1462 user2 = insert(:user)
1463
1464 {:ok, user, user2} = User.follow(user, user2)
1465 {:ok, _user} = User.set_activation(user, false)
1466
1467 user2 = User.get_cached_by_id(user2.id)
1468
1469 assert user2.follower_count == 0
1470 assert [] = User.get_followers(user2)
1471 end
1472
1473 test "hide a user from friends" do
1474 user = insert(:user)
1475 user2 = insert(:user)
1476
1477 {:ok, user2, user} = User.follow(user2, user)
1478 assert user2.following_count == 1
1479 assert User.following_count(user2) == 1
1480
1481 {:ok, _user} = User.set_activation(user, false)
1482
1483 user2 = User.get_cached_by_id(user2.id)
1484
1485 assert refresh_record(user2).following_count == 0
1486 assert user2.following_count == 0
1487 assert User.following_count(user2) == 0
1488 assert [] = User.get_friends(user2)
1489 end
1490
1491 test "hide a user's statuses from timelines and notifications" do
1492 user = insert(:user)
1493 user2 = insert(:user)
1494
1495 {:ok, user2, user} = User.follow(user2, user)
1496
1497 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{user2.nickname}"})
1498
1499 activity = Repo.preload(activity, :bookmark)
1500
1501 [notification] = Pleroma.Notification.for_user(user2)
1502 assert notification.activity.id == activity.id
1503
1504 assert [activity] == ActivityPub.fetch_public_activities(%{}) |> Repo.preload(:bookmark)
1505
1506 assert [%{activity | thread_muted?: CommonAPI.thread_muted?(user2, activity)}] ==
1507 ActivityPub.fetch_activities([user2.ap_id | User.following(user2)], %{
1508 user: user2
1509 })
1510
1511 {:ok, _user} = User.set_activation(user, false)
1512
1513 assert [] == ActivityPub.fetch_public_activities(%{})
1514 assert [] == Pleroma.Notification.for_user(user2)
1515
1516 assert [] ==
1517 ActivityPub.fetch_activities([user2.ap_id | User.following(user2)], %{
1518 user: user2
1519 })
1520 end
1521 end
1522
1523 describe "approve" do
1524 test "approves a user" do
1525 user = insert(:user, is_approved: false)
1526 refute user.is_approved
1527 {:ok, user} = User.approve(user)
1528 assert user.is_approved
1529 end
1530
1531 test "approves a list of users" do
1532 unapproved_users = [
1533 insert(:user, is_approved: false),
1534 insert(:user, is_approved: false),
1535 insert(:user, is_approved: false)
1536 ]
1537
1538 {:ok, users} = User.approve(unapproved_users)
1539
1540 assert Enum.count(users) == 3
1541
1542 Enum.each(users, fn user ->
1543 assert user.is_approved
1544 end)
1545 end
1546
1547 test "it sends welcome email if it is set" do
1548 clear_config([:welcome, :email, :enabled], true)
1549 clear_config([:welcome, :email, :sender], "tester@test.me")
1550
1551 user = insert(:user, is_approved: false)
1552 welcome_user = insert(:user, email: "tester@test.me")
1553 instance_name = Pleroma.Config.get([:instance, :name])
1554
1555 User.approve(user)
1556
1557 ObanHelpers.perform_all()
1558
1559 assert_email_sent(
1560 from: {instance_name, welcome_user.email},
1561 to: {user.name, user.email},
1562 html_body: "Welcome to #{instance_name}"
1563 )
1564 end
1565
1566 test "approving an approved user does not trigger post-register actions" do
1567 clear_config([:welcome, :email, :enabled], true)
1568
1569 user = insert(:user, is_approved: true)
1570 User.approve(user)
1571
1572 ObanHelpers.perform_all()
1573
1574 assert_no_email_sent()
1575 end
1576 end
1577
1578 describe "confirm" do
1579 test "confirms a user" do
1580 user = insert(:user, is_confirmed: false)
1581 refute user.is_confirmed
1582 {:ok, user} = User.confirm(user)
1583 assert user.is_confirmed
1584 end
1585
1586 test "confirms a list of users" do
1587 unconfirmed_users = [
1588 insert(:user, is_confirmed: false),
1589 insert(:user, is_confirmed: false),
1590 insert(:user, is_confirmed: false)
1591 ]
1592
1593 {:ok, users} = User.confirm(unconfirmed_users)
1594
1595 assert Enum.count(users) == 3
1596
1597 Enum.each(users, fn user ->
1598 assert user.is_confirmed
1599 end)
1600 end
1601
1602 test "sends approval emails when `is_approved: false`" do
1603 admin = insert(:user, is_admin: true)
1604 user = insert(:user, is_confirmed: false, is_approved: false)
1605 User.confirm(user)
1606
1607 ObanHelpers.perform_all()
1608
1609 user_email = Pleroma.Emails.UserEmail.approval_pending_email(user)
1610 admin_email = Pleroma.Emails.AdminEmail.new_unapproved_registration(admin, user)
1611
1612 notify_email = Pleroma.Config.get([:instance, :notify_email])
1613 instance_name = Pleroma.Config.get([:instance, :name])
1614
1615 # User approval email
1616 assert_email_sent(
1617 from: {instance_name, notify_email},
1618 to: {user.name, user.email},
1619 html_body: user_email.html_body
1620 )
1621
1622 # Admin email
1623 assert_email_sent(
1624 from: {instance_name, notify_email},
1625 to: {admin.name, admin.email},
1626 html_body: admin_email.html_body
1627 )
1628 end
1629
1630 test "confirming a confirmed user does not trigger post-register actions" do
1631 user = insert(:user, is_confirmed: true, is_approved: false)
1632 User.confirm(user)
1633
1634 ObanHelpers.perform_all()
1635
1636 assert_no_email_sent()
1637 end
1638 end
1639
1640 describe "delete" do
1641 setup do
1642 {:ok, user} = insert(:user) |> User.set_cache()
1643
1644 [user: user]
1645 end
1646
1647 setup do: clear_config([:instance, :federating])
1648
1649 test ".delete_user_activities deletes all create activities", %{user: user} do
1650 {:ok, activity} = CommonAPI.post(user, %{status: "2hu"})
1651
1652 User.delete_user_activities(user)
1653
1654 # TODO: Test removal favorites, repeats, delete activities.
1655 refute Activity.get_by_id(activity.id)
1656 end
1657
1658 test "it deactivates a user, all follow relationships and all activities", %{user: user} do
1659 follower = insert(:user)
1660 {:ok, follower, user} = User.follow(follower, user)
1661
1662 locked_user = insert(:user, name: "locked", is_locked: true)
1663 {:ok, _, _} = User.follow(user, locked_user, :follow_pending)
1664
1665 object = insert(:note, user: user)
1666 activity = insert(:note_activity, user: user, note: object)
1667
1668 object_two = insert(:note, user: follower)
1669 activity_two = insert(:note_activity, user: follower, note: object_two)
1670
1671 {:ok, like} = CommonAPI.favorite(user, activity_two.id)
1672 {:ok, like_two} = CommonAPI.favorite(follower, activity.id)
1673 {:ok, repeat} = CommonAPI.repeat(activity_two.id, user)
1674
1675 {:ok, job} = User.delete(user)
1676 {:ok, _user} = ObanHelpers.perform(job)
1677
1678 follower = User.get_cached_by_id(follower.id)
1679
1680 refute User.following?(follower, user)
1681 assert %{is_active: false} = User.get_by_id(user.id)
1682
1683 assert [] == User.get_follow_requests(locked_user)
1684
1685 user_activities =
1686 user.ap_id
1687 |> Activity.Queries.by_actor()
1688 |> Repo.all()
1689 |> Enum.map(fn act -> act.data["type"] end)
1690
1691 assert Enum.all?(user_activities, fn act -> act in ~w(Delete Undo) end)
1692
1693 refute Activity.get_by_id(activity.id)
1694 refute Activity.get_by_id(like.id)
1695 refute Activity.get_by_id(like_two.id)
1696 refute Activity.get_by_id(repeat.id)
1697 end
1698 end
1699
1700 test "delete/1 when confirmation is pending deletes the user" do
1701 clear_config([:instance, :account_activation_required], true)
1702 user = insert(:user, is_confirmed: false)
1703
1704 {:ok, job} = User.delete(user)
1705 {:ok, _} = ObanHelpers.perform(job)
1706
1707 refute User.get_cached_by_id(user.id)
1708 refute User.get_by_id(user.id)
1709 end
1710
1711 test "delete/1 when approval is pending deletes the user" do
1712 user = insert(:user, is_approved: false)
1713
1714 {:ok, job} = User.delete(user)
1715 {:ok, _} = ObanHelpers.perform(job)
1716
1717 refute User.get_cached_by_id(user.id)
1718 refute User.get_by_id(user.id)
1719 end
1720
1721 test "delete/1 purges a user when they wouldn't be fully deleted" do
1722 user =
1723 insert(:user, %{
1724 bio: "eyy lmao",
1725 name: "qqqqqqq",
1726 password_hash: "pdfk2$1b3n159001",
1727 keys: "RSA begin buplic key",
1728 public_key: "--PRIVATE KEYE--",
1729 avatar: %{"a" => "b"},
1730 tags: ["qqqqq"],
1731 banner: %{"a" => "b"},
1732 background: %{"a" => "b"},
1733 note_count: 9,
1734 follower_count: 9,
1735 following_count: 9001,
1736 is_locked: true,
1737 is_confirmed: true,
1738 password_reset_pending: true,
1739 is_approved: true,
1740 registration_reason: "ahhhhh",
1741 confirmation_token: "qqqq",
1742 domain_blocks: ["lain.com"],
1743 is_active: false,
1744 ap_enabled: true,
1745 is_moderator: true,
1746 is_admin: true,
1747 mastofe_settings: %{"a" => "b"},
1748 mascot: %{"a" => "b"},
1749 emoji: %{"a" => "b"},
1750 pleroma_settings_store: %{"q" => "x"},
1751 fields: [%{"gg" => "qq"}],
1752 raw_fields: [%{"gg" => "qq"}],
1753 is_discoverable: true,
1754 also_known_as: ["https://lol.olo/users/loll"]
1755 })
1756
1757 {:ok, job} = User.delete(user)
1758 {:ok, _} = ObanHelpers.perform(job)
1759 user = User.get_by_id(user.id)
1760
1761 assert %User{
1762 bio: "",
1763 raw_bio: nil,
1764 email: nil,
1765 name: nil,
1766 password_hash: nil,
1767 keys: "RSA begin buplic key",
1768 public_key: "--PRIVATE KEYE--",
1769 avatar: %{},
1770 tags: [],
1771 last_refreshed_at: nil,
1772 last_digest_emailed_at: nil,
1773 banner: %{},
1774 background: %{},
1775 note_count: 0,
1776 follower_count: 0,
1777 following_count: 0,
1778 is_locked: false,
1779 is_confirmed: true,
1780 password_reset_pending: false,
1781 is_approved: true,
1782 registration_reason: nil,
1783 confirmation_token: nil,
1784 domain_blocks: [],
1785 is_active: false,
1786 ap_enabled: false,
1787 is_moderator: false,
1788 is_admin: false,
1789 mastofe_settings: nil,
1790 mascot: nil,
1791 emoji: %{},
1792 pleroma_settings_store: %{},
1793 fields: [],
1794 raw_fields: [],
1795 is_discoverable: false,
1796 also_known_as: []
1797 } = user
1798 end
1799
1800 test "delete/1 purges a remote user" do
1801 user =
1802 insert(:user, %{
1803 name: "qqqqqqq",
1804 avatar: %{"a" => "b"},
1805 banner: %{"a" => "b"},
1806 local: false
1807 })
1808
1809 {:ok, job} = User.delete(user)
1810 {:ok, _} = ObanHelpers.perform(job)
1811 user = User.get_by_id(user.id)
1812
1813 assert user.name == nil
1814 assert user.avatar == %{}
1815 assert user.banner == %{}
1816 end
1817
1818 describe "set_suggestion" do
1819 test "suggests a user" do
1820 user = insert(:user, is_suggested: false)
1821 refute user.is_suggested
1822 {:ok, user} = User.set_suggestion(user, true)
1823 assert user.is_suggested
1824 end
1825
1826 test "suggests a list of users" do
1827 unsuggested_users = [
1828 insert(:user, is_suggested: false),
1829 insert(:user, is_suggested: false),
1830 insert(:user, is_suggested: false)
1831 ]
1832
1833 {:ok, users} = User.set_suggestion(unsuggested_users, true)
1834
1835 assert Enum.count(users) == 3
1836
1837 Enum.each(users, fn user ->
1838 assert user.is_suggested
1839 end)
1840 end
1841
1842 test "unsuggests a user" do
1843 user = insert(:user, is_suggested: true)
1844 assert user.is_suggested
1845 {:ok, user} = User.set_suggestion(user, false)
1846 refute user.is_suggested
1847 end
1848 end
1849
1850 test "get_public_key_for_ap_id fetches a user that's not in the db" do
1851 assert {:ok, _key} = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin")
1852 end
1853
1854 describe "per-user rich-text filtering" do
1855 test "html_filter_policy returns default policies, when rich-text is enabled" do
1856 user = insert(:user)
1857
1858 assert Pleroma.Config.get([:markup, :scrub_policy]) == User.html_filter_policy(user)
1859 end
1860
1861 test "html_filter_policy returns TwitterText scrubber when rich-text is disabled" do
1862 user = insert(:user, no_rich_text: true)
1863
1864 assert Pleroma.HTML.Scrubber.TwitterText == User.html_filter_policy(user)
1865 end
1866 end
1867
1868 describe "caching" do
1869 test "invalidate_cache works" do
1870 user = insert(:user)
1871
1872 User.set_cache(user)
1873 User.invalidate_cache(user)
1874
1875 {:ok, nil} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
1876 {:ok, nil} = Cachex.get(:user_cache, "nickname:#{user.nickname}")
1877 end
1878
1879 test "User.delete() plugs any possible zombie objects" do
1880 user = insert(:user)
1881
1882 {:ok, job} = User.delete(user)
1883 {:ok, _} = ObanHelpers.perform(job)
1884
1885 {:ok, cached_user} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
1886
1887 assert cached_user != user
1888
1889 {:ok, cached_user} = Cachex.get(:user_cache, "nickname:#{user.ap_id}")
1890
1891 assert cached_user != user
1892 end
1893 end
1894
1895 describe "account_status/1" do
1896 setup do: clear_config([:instance, :account_activation_required])
1897
1898 test "return confirmation_pending for unconfirm user" do
1899 clear_config([:instance, :account_activation_required], true)
1900 user = insert(:user, is_confirmed: false)
1901 assert User.account_status(user) == :confirmation_pending
1902 end
1903
1904 test "return active for confirmed user" do
1905 clear_config([:instance, :account_activation_required], true)
1906 user = insert(:user, is_confirmed: true)
1907 assert User.account_status(user) == :active
1908 end
1909
1910 test "return active for remote user" do
1911 user = insert(:user, local: false)
1912 assert User.account_status(user) == :active
1913 end
1914
1915 test "returns :password_reset_pending for user with reset password" do
1916 user = insert(:user, password_reset_pending: true)
1917 assert User.account_status(user) == :password_reset_pending
1918 end
1919
1920 test "returns :deactivated for deactivated user" do
1921 user = insert(:user, local: true, is_confirmed: true, is_active: false)
1922 assert User.account_status(user) == :deactivated
1923 end
1924
1925 test "returns :approval_pending for unapproved user" do
1926 user = insert(:user, local: true, is_approved: false)
1927 assert User.account_status(user) == :approval_pending
1928
1929 user = insert(:user, local: true, is_confirmed: false, is_approved: false)
1930 assert User.account_status(user) == :approval_pending
1931 end
1932 end
1933
1934 describe "superuser?/1" do
1935 test "returns false for unprivileged users" do
1936 user = insert(:user, local: true)
1937
1938 refute User.superuser?(user)
1939 end
1940
1941 test "returns false for remote users" do
1942 user = insert(:user, local: false)
1943 remote_admin_user = insert(:user, local: false, is_admin: true)
1944
1945 refute User.superuser?(user)
1946 refute User.superuser?(remote_admin_user)
1947 end
1948
1949 test "returns true for local moderators" do
1950 user = insert(:user, local: true, is_moderator: true)
1951
1952 assert User.superuser?(user)
1953 end
1954
1955 test "returns true for local admins" do
1956 user = insert(:user, local: true, is_admin: true)
1957
1958 assert User.superuser?(user)
1959 end
1960 end
1961
1962 describe "invisible?/1" do
1963 test "returns true for an invisible user" do
1964 user = insert(:user, local: true, invisible: true)
1965
1966 assert User.invisible?(user)
1967 end
1968
1969 test "returns false for a non-invisible user" do
1970 user = insert(:user, local: true)
1971
1972 refute User.invisible?(user)
1973 end
1974 end
1975
1976 describe "visible_for/2" do
1977 test "returns true when the account is itself" do
1978 user = insert(:user, local: true)
1979
1980 assert User.visible_for(user, user) == :visible
1981 end
1982
1983 test "returns false when the account is unconfirmed and confirmation is required" do
1984 clear_config([:instance, :account_activation_required], true)
1985
1986 user = insert(:user, local: true, is_confirmed: false)
1987 other_user = insert(:user, local: true)
1988
1989 refute User.visible_for(user, other_user) == :visible
1990 end
1991
1992 test "returns true when the account is unconfirmed and confirmation is required but the account is remote" do
1993 clear_config([:instance, :account_activation_required], true)
1994
1995 user = insert(:user, local: false, is_confirmed: false)
1996 other_user = insert(:user, local: true)
1997
1998 assert User.visible_for(user, other_user) == :visible
1999 end
2000
2001 test "returns true when the account is unconfirmed and being viewed by a privileged account (confirmation required)" do
2002 clear_config([:instance, :account_activation_required], true)
2003
2004 user = insert(:user, local: true, is_confirmed: false)
2005 other_user = insert(:user, local: true, is_admin: true)
2006
2007 assert User.visible_for(user, other_user) == :visible
2008 end
2009 end
2010
2011 describe "parse_bio/2" do
2012 test "preserves hosts in user links text" do
2013 remote_user = insert(:user, local: false, nickname: "nick@domain.com")
2014 user = insert(:user)
2015 bio = "A.k.a. @nick@domain.com"
2016
2017 expected_text =
2018 ~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>)
2019
2020 assert expected_text == User.parse_bio(bio, user)
2021 end
2022
2023 test "Adds rel=me on linkbacked urls" do
2024 user = insert(:user, ap_id: "https://social.example.org/users/lain")
2025
2026 bio = "http://example.com/rel_me/null"
2027 expected_text = "<a href=\"#{bio}\">#{bio}</a>"
2028 assert expected_text == User.parse_bio(bio, user)
2029
2030 bio = "http://example.com/rel_me/link"
2031 expected_text = "<a href=\"#{bio}\" rel=\"me\">#{bio}</a>"
2032 assert expected_text == User.parse_bio(bio, user)
2033
2034 bio = "http://example.com/rel_me/anchor"
2035 expected_text = "<a href=\"#{bio}\" rel=\"me\">#{bio}</a>"
2036 assert expected_text == User.parse_bio(bio, user)
2037 end
2038 end
2039
2040 test "follower count is updated when a follower is blocked" do
2041 user = insert(:user)
2042 follower = insert(:user)
2043 follower2 = insert(:user)
2044 follower3 = insert(:user)
2045
2046 {:ok, follower, user} = User.follow(follower, user)
2047 {:ok, _follower2, _user} = User.follow(follower2, user)
2048 {:ok, _follower3, _user} = User.follow(follower3, user)
2049
2050 {:ok, _user_relationship} = User.block(user, follower)
2051 user = refresh_record(user)
2052
2053 assert user.follower_count == 2
2054 end
2055
2056 describe "list_inactive_users_query/1" do
2057 defp days_ago(days) do
2058 NaiveDateTime.add(
2059 NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second),
2060 -days * 60 * 60 * 24,
2061 :second
2062 )
2063 end
2064
2065 test "Users are inactive by default" do
2066 total = 10
2067
2068 users =
2069 Enum.map(1..total, fn _ ->
2070 insert(:user, last_digest_emailed_at: days_ago(20), is_active: true)
2071 end)
2072
2073 inactive_users_ids =
2074 Pleroma.User.list_inactive_users_query()
2075 |> Pleroma.Repo.all()
2076 |> Enum.map(& &1.id)
2077
2078 Enum.each(users, fn user ->
2079 assert user.id in inactive_users_ids
2080 end)
2081 end
2082
2083 test "Only includes users who has no recent activity" do
2084 total = 10
2085
2086 users =
2087 Enum.map(1..total, fn _ ->
2088 insert(:user, last_digest_emailed_at: days_ago(20), is_active: true)
2089 end)
2090
2091 {inactive, active} = Enum.split(users, trunc(total / 2))
2092
2093 Enum.map(active, fn user ->
2094 to = Enum.random(users -- [user])
2095
2096 {:ok, _} =
2097 CommonAPI.post(user, %{
2098 status: "hey @#{to.nickname}"
2099 })
2100 end)
2101
2102 inactive_users_ids =
2103 Pleroma.User.list_inactive_users_query()
2104 |> Pleroma.Repo.all()
2105 |> Enum.map(& &1.id)
2106
2107 Enum.each(active, fn user ->
2108 refute user.id in inactive_users_ids
2109 end)
2110
2111 Enum.each(inactive, fn user ->
2112 assert user.id in inactive_users_ids
2113 end)
2114 end
2115
2116 test "Only includes users with no read notifications" do
2117 total = 10
2118
2119 users =
2120 Enum.map(1..total, fn _ ->
2121 insert(:user, last_digest_emailed_at: days_ago(20), is_active: true)
2122 end)
2123
2124 [sender | recipients] = users
2125 {inactive, active} = Enum.split(recipients, trunc(total / 2))
2126
2127 Enum.each(recipients, fn to ->
2128 {:ok, _} =
2129 CommonAPI.post(sender, %{
2130 status: "hey @#{to.nickname}"
2131 })
2132
2133 {:ok, _} =
2134 CommonAPI.post(sender, %{
2135 status: "hey again @#{to.nickname}"
2136 })
2137 end)
2138
2139 Enum.each(active, fn user ->
2140 [n1, _n2] = Pleroma.Notification.for_user(user)
2141 {:ok, _} = Pleroma.Notification.read_one(user, n1.id)
2142 end)
2143
2144 inactive_users_ids =
2145 Pleroma.User.list_inactive_users_query()
2146 |> Pleroma.Repo.all()
2147 |> Enum.map(& &1.id)
2148
2149 Enum.each(active, fn user ->
2150 refute user.id in inactive_users_ids
2151 end)
2152
2153 Enum.each(inactive, fn user ->
2154 assert user.id in inactive_users_ids
2155 end)
2156 end
2157 end
2158
2159 describe "get_ap_ids_by_nicknames" do
2160 test "it returns a list of AP ids for a given set of nicknames" do
2161 user = insert(:user)
2162 user_two = insert(:user)
2163
2164 ap_ids = User.get_ap_ids_by_nicknames([user.nickname, user_two.nickname, "nonexistent"])
2165 assert length(ap_ids) == 2
2166 assert user.ap_id in ap_ids
2167 assert user_two.ap_id in ap_ids
2168 end
2169 end
2170
2171 describe "sync followers count" do
2172 setup do
2173 user1 = insert(:user, local: false, ap_id: "http://localhost:4001/users/masto_closed")
2174 user2 = insert(:user, local: false, ap_id: "http://localhost:4001/users/fuser2")
2175 insert(:user, local: true)
2176 insert(:user, local: false, is_active: false)
2177 {:ok, user1: user1, user2: user2}
2178 end
2179
2180 test "external_users/1 external active users with limit", %{user1: user1, user2: user2} do
2181 [fdb_user1] = User.external_users(limit: 1)
2182
2183 assert fdb_user1.ap_id
2184 assert fdb_user1.ap_id == user1.ap_id
2185 assert fdb_user1.id == user1.id
2186
2187 [fdb_user2] = User.external_users(max_id: fdb_user1.id, limit: 1)
2188
2189 assert fdb_user2.ap_id
2190 assert fdb_user2.ap_id == user2.ap_id
2191 assert fdb_user2.id == user2.id
2192
2193 assert User.external_users(max_id: fdb_user2.id, limit: 1) == []
2194 end
2195 end
2196
2197 describe "is_internal_user?/1" do
2198 test "non-internal user returns false" do
2199 user = insert(:user)
2200 refute User.is_internal_user?(user)
2201 end
2202
2203 test "user with no nickname returns true" do
2204 user = insert(:user, %{nickname: nil})
2205 assert User.is_internal_user?(user)
2206 end
2207
2208 test "user with internal-prefixed nickname returns true" do
2209 user = insert(:user, %{nickname: "internal.test"})
2210 assert User.is_internal_user?(user)
2211 end
2212 end
2213
2214 describe "update_and_set_cache/1" do
2215 test "returns error when user is stale instead Ecto.StaleEntryError" do
2216 user = insert(:user)
2217
2218 changeset = Ecto.Changeset.change(user, bio: "test")
2219
2220 Repo.delete(user)
2221
2222 assert {:error, %Ecto.Changeset{errors: [id: {"is stale", [stale: true]}], valid?: false}} =
2223 User.update_and_set_cache(changeset)
2224 end
2225
2226 test "performs update cache if user updated" do
2227 user = insert(:user)
2228 assert {:ok, nil} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
2229
2230 changeset = Ecto.Changeset.change(user, bio: "test-bio")
2231
2232 assert {:ok, %User{bio: "test-bio"} = user} = User.update_and_set_cache(changeset)
2233 assert {:ok, user} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
2234 assert %User{bio: "test-bio"} = User.get_cached_by_ap_id(user.ap_id)
2235 end
2236
2237 test "removes report notifs when user isn't superuser any more" do
2238 report_activity = insert(:report_activity)
2239 user = insert(:user, is_moderator: true, is_admin: true)
2240 {:ok, _} = Notification.create_notifications(report_activity)
2241
2242 assert [%Pleroma.Notification{type: "pleroma:report"}] = Notification.for_user(user)
2243
2244 {:ok, user} = user |> User.admin_api_update(%{is_moderator: false})
2245 # is still superuser because still admin
2246 assert [%Pleroma.Notification{type: "pleroma:report"}] = Notification.for_user(user)
2247
2248 {:ok, user} = user |> User.admin_api_update(%{is_moderator: true, is_admin: false})
2249 # is still superuser because still moderator
2250 assert [%Pleroma.Notification{type: "pleroma:report"}] = Notification.for_user(user)
2251
2252 {:ok, user} = user |> User.admin_api_update(%{is_moderator: false})
2253 # is not a superuser any more
2254 assert [] = Notification.for_user(user)
2255 end
2256 end
2257
2258 describe "following/followers synchronization" do
2259 setup do: clear_config([:instance, :external_user_synchronization])
2260
2261 test "updates the counters normally on following/getting a follow when disabled" do
2262 clear_config([:instance, :external_user_synchronization], false)
2263 user = insert(:user)
2264
2265 other_user =
2266 insert(:user,
2267 local: false,
2268 follower_address: "http://localhost:4001/users/masto_closed/followers",
2269 following_address: "http://localhost:4001/users/masto_closed/following",
2270 ap_enabled: true
2271 )
2272
2273 assert other_user.following_count == 0
2274 assert other_user.follower_count == 0
2275
2276 {:ok, user, other_user} = Pleroma.User.follow(user, other_user)
2277
2278 assert user.following_count == 1
2279 assert other_user.follower_count == 1
2280 end
2281
2282 test "synchronizes the counters with the remote instance for the followed when enabled" do
2283 clear_config([:instance, :external_user_synchronization], false)
2284
2285 user = insert(:user)
2286
2287 other_user =
2288 insert(:user,
2289 local: false,
2290 follower_address: "http://localhost:4001/users/masto_closed/followers",
2291 following_address: "http://localhost:4001/users/masto_closed/following",
2292 ap_enabled: true
2293 )
2294
2295 assert other_user.following_count == 0
2296 assert other_user.follower_count == 0
2297
2298 clear_config([:instance, :external_user_synchronization], true)
2299 {:ok, _user, other_user} = User.follow(user, other_user)
2300
2301 assert other_user.follower_count == 437
2302 end
2303
2304 test "synchronizes the counters with the remote instance for the follower when enabled" do
2305 clear_config([:instance, :external_user_synchronization], false)
2306
2307 user = insert(:user)
2308
2309 other_user =
2310 insert(:user,
2311 local: false,
2312 follower_address: "http://localhost:4001/users/masto_closed/followers",
2313 following_address: "http://localhost:4001/users/masto_closed/following",
2314 ap_enabled: true
2315 )
2316
2317 assert other_user.following_count == 0
2318 assert other_user.follower_count == 0
2319
2320 clear_config([:instance, :external_user_synchronization], true)
2321 {:ok, other_user, _user} = User.follow(other_user, user)
2322
2323 assert other_user.following_count == 152
2324 end
2325 end
2326
2327 describe "change_email/2" do
2328 setup do
2329 [user: insert(:user)]
2330 end
2331
2332 test "blank email returns error if we require an email on registration", %{user: user} do
2333 orig_account_activation_required =
2334 Pleroma.Config.get([:instance, :account_activation_required])
2335
2336 Pleroma.Config.put([:instance, :account_activation_required], true)
2337
2338 on_exit(fn ->
2339 Pleroma.Config.put(
2340 [:instance, :account_activation_required],
2341 orig_account_activation_required
2342 )
2343 end)
2344
2345 assert {:error, %{errors: [email: {"can't be blank", _}]}} = User.change_email(user, "")
2346 assert {:error, %{errors: [email: {"can't be blank", _}]}} = User.change_email(user, nil)
2347 end
2348
2349 test "blank email should be fine if we do not require an email on registration", %{user: user} do
2350 orig_account_activation_required =
2351 Pleroma.Config.get([:instance, :account_activation_required])
2352
2353 Pleroma.Config.put([:instance, :account_activation_required], false)
2354
2355 on_exit(fn ->
2356 Pleroma.Config.put(
2357 [:instance, :account_activation_required],
2358 orig_account_activation_required
2359 )
2360 end)
2361
2362 assert {:ok, %User{email: nil}} = User.change_email(user, "")
2363 assert {:ok, %User{email: nil}} = User.change_email(user, nil)
2364 end
2365
2366 test "non unique email returns error", %{user: user} do
2367 %{email: email} = insert(:user)
2368
2369 assert {:error, %{errors: [email: {"has already been taken", _}]}} =
2370 User.change_email(user, email)
2371 end
2372
2373 test "invalid email returns error", %{user: user} do
2374 assert {:error, %{errors: [email: {"has invalid format", _}]}} =
2375 User.change_email(user, "cofe")
2376 end
2377
2378 test "changes email", %{user: user} do
2379 assert {:ok, %User{email: "cofe@cofe.party"}} = User.change_email(user, "cofe@cofe.party")
2380 end
2381
2382 test "adds email", %{user: user} do
2383 orig_account_activation_required =
2384 Pleroma.Config.get([:instance, :account_activation_required])
2385
2386 Pleroma.Config.put([:instance, :account_activation_required], false)
2387
2388 on_exit(fn ->
2389 Pleroma.Config.put(
2390 [:instance, :account_activation_required],
2391 orig_account_activation_required
2392 )
2393 end)
2394
2395 assert {:ok, _} = User.change_email(user, "")
2396 Pleroma.Config.put([:instance, :account_activation_required], true)
2397
2398 assert {:ok, %User{email: "cofe2@cofe.party"}} = User.change_email(user, "cofe2@cofe.party")
2399 end
2400 end
2401
2402 describe "get_cached_by_nickname_or_id" do
2403 setup do
2404 local_user = insert(:user)
2405 remote_user = insert(:user, nickname: "nickname@example.com", local: false)
2406
2407 [local_user: local_user, remote_user: remote_user]
2408 end
2409
2410 setup do: clear_config([:instance, :limit_to_local_content])
2411
2412 test "allows getting remote users by id no matter what :limit_to_local_content is set to", %{
2413 remote_user: remote_user
2414 } do
2415 clear_config([:instance, :limit_to_local_content], false)
2416 assert %User{} = User.get_cached_by_nickname_or_id(remote_user.id)
2417
2418 clear_config([:instance, :limit_to_local_content], true)
2419 assert %User{} = User.get_cached_by_nickname_or_id(remote_user.id)
2420
2421 clear_config([:instance, :limit_to_local_content], :unauthenticated)
2422 assert %User{} = User.get_cached_by_nickname_or_id(remote_user.id)
2423 end
2424
2425 test "disallows getting remote users by nickname without authentication when :limit_to_local_content is set to :unauthenticated",
2426 %{remote_user: remote_user} do
2427 clear_config([:instance, :limit_to_local_content], :unauthenticated)
2428 assert nil == User.get_cached_by_nickname_or_id(remote_user.nickname)
2429 end
2430
2431 test "allows getting remote users by nickname with authentication when :limit_to_local_content is set to :unauthenticated",
2432 %{remote_user: remote_user, local_user: local_user} do
2433 clear_config([:instance, :limit_to_local_content], :unauthenticated)
2434 assert %User{} = User.get_cached_by_nickname_or_id(remote_user.nickname, for: local_user)
2435 end
2436
2437 test "disallows getting remote users by nickname when :limit_to_local_content is set to true",
2438 %{remote_user: remote_user} do
2439 clear_config([:instance, :limit_to_local_content], true)
2440 assert nil == User.get_cached_by_nickname_or_id(remote_user.nickname)
2441 end
2442
2443 test "allows getting local users by nickname no matter what :limit_to_local_content is set to",
2444 %{local_user: local_user} do
2445 clear_config([:instance, :limit_to_local_content], false)
2446 assert %User{} = User.get_cached_by_nickname_or_id(local_user.nickname)
2447
2448 clear_config([:instance, :limit_to_local_content], true)
2449 assert %User{} = User.get_cached_by_nickname_or_id(local_user.nickname)
2450
2451 clear_config([:instance, :limit_to_local_content], :unauthenticated)
2452 assert %User{} = User.get_cached_by_nickname_or_id(local_user.nickname)
2453 end
2454 end
2455
2456 describe "update_email_notifications/2" do
2457 setup do
2458 user = insert(:user, email_notifications: %{"digest" => true})
2459
2460 {:ok, user: user}
2461 end
2462
2463 test "Notifications are updated", %{user: user} do
2464 true = user.email_notifications["digest"]
2465 assert {:ok, result} = User.update_email_notifications(user, %{"digest" => false})
2466 assert result.email_notifications["digest"] == false
2467 end
2468 end
2469
2470 describe "local_nickname/1" do
2471 test "returns nickname without host" do
2472 assert User.local_nickname("@mentioned") == "mentioned"
2473 assert User.local_nickname("a_local_nickname") == "a_local_nickname"
2474 assert User.local_nickname("nickname@host.com") == "nickname"
2475 end
2476 end
2477
2478 describe "full_nickname/1" do
2479 test "returns fully qualified nickname for local and remote users" do
2480 local_user =
2481 insert(:user, nickname: "local_user", ap_id: "https://somehost.com/users/local_user")
2482
2483 remote_user = insert(:user, nickname: "remote@host.com", local: false)
2484
2485 assert User.full_nickname(local_user) == "local_user@somehost.com"
2486 assert User.full_nickname(remote_user) == "remote@host.com"
2487 end
2488
2489 test "strips leading @ from mentions" do
2490 assert User.full_nickname("@mentioned") == "mentioned"
2491 assert User.full_nickname("@nickname@host.com") == "nickname@host.com"
2492 end
2493
2494 test "does not modify nicknames" do
2495 assert User.full_nickname("nickname") == "nickname"
2496 assert User.full_nickname("nickname@host.com") == "nickname@host.com"
2497 end
2498 end
2499
2500 test "avatar fallback" do
2501 user = insert(:user)
2502 assert User.avatar_url(user) =~ "/images/avi.png"
2503
2504 clear_config([:assets, :default_user_avatar], "avatar.png")
2505
2506 user = User.get_cached_by_nickname_or_id(user.nickname)
2507 assert User.avatar_url(user) =~ "avatar.png"
2508
2509 assert User.avatar_url(user, no_default: true) == nil
2510 end
2511
2512 test "get_host/1" do
2513 user = insert(:user, ap_id: "https://lain.com/users/lain", nickname: "lain")
2514 assert User.get_host(user) == "lain.com"
2515 end
2516
2517 test "update_last_active_at/1" do
2518 user = insert(:user)
2519 assert is_nil(user.last_active_at)
2520
2521 test_started_at = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
2522
2523 assert {:ok, user} = User.update_last_active_at(user)
2524
2525 assert user.last_active_at >= test_started_at
2526 assert user.last_active_at <= NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
2527
2528 last_active_at =
2529 NaiveDateTime.utc_now()
2530 |> NaiveDateTime.add(-:timer.hours(24), :millisecond)
2531 |> NaiveDateTime.truncate(:second)
2532
2533 assert {:ok, user} =
2534 user
2535 |> cast(%{last_active_at: last_active_at}, [:last_active_at])
2536 |> User.update_and_set_cache()
2537
2538 assert user.last_active_at == last_active_at
2539 assert {:ok, user} = User.update_last_active_at(user)
2540 assert user.last_active_at >= test_started_at
2541 assert user.last_active_at <= NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
2542 end
2543
2544 test "active_user_count/1" do
2545 insert(:user)
2546 insert(:user, %{local: false})
2547 insert(:user, %{last_active_at: NaiveDateTime.utc_now()})
2548 insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), days: -15)})
2549 insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), weeks: -6)})
2550 insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), months: -7)})
2551 insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), years: -2)})
2552
2553 assert User.active_user_count() == 2
2554 assert User.active_user_count(180) == 3
2555 assert User.active_user_count(365) == 4
2556 assert User.active_user_count(1000) == 5
2557 end
2558
2559 describe "pins" do
2560 setup do
2561 user = insert(:user)
2562
2563 [user: user, object_id: object_id_from_created_activity(user)]
2564 end
2565
2566 test "unique pins", %{user: user, object_id: object_id} do
2567 assert {:ok, %{pinned_objects: %{^object_id => pinned_at1} = pins} = updated_user} =
2568 User.add_pinned_object_id(user, object_id)
2569
2570 assert Enum.count(pins) == 1
2571
2572 assert {:ok, %{pinned_objects: %{^object_id => pinned_at2} = pins}} =
2573 User.add_pinned_object_id(updated_user, object_id)
2574
2575 assert pinned_at1 == pinned_at2
2576
2577 assert Enum.count(pins) == 1
2578 end
2579
2580 test "respects max_pinned_statuses limit", %{user: user, object_id: object_id} do
2581 clear_config([:instance, :max_pinned_statuses], 1)
2582 {:ok, updated} = User.add_pinned_object_id(user, object_id)
2583
2584 object_id2 = object_id_from_created_activity(user)
2585
2586 {:error, %{errors: errors}} = User.add_pinned_object_id(updated, object_id2)
2587 assert Keyword.has_key?(errors, :pinned_objects)
2588 end
2589
2590 test "remove_pinned_object_id/2", %{user: user, object_id: object_id} do
2591 assert {:ok, updated} = User.add_pinned_object_id(user, object_id)
2592
2593 {:ok, after_remove} = User.remove_pinned_object_id(updated, object_id)
2594 assert after_remove.pinned_objects == %{}
2595 end
2596 end
2597
2598 defp object_id_from_created_activity(user) do
2599 %{id: id} = insert(:note_activity, user: user)
2600 %{object: %{data: %{"id" => object_id}}} = Activity.get_by_id_with_object(id)
2601 object_id
2602 end
2603
2604 describe "add_alias/2" do
2605 test "should add alias for another user" do
2606 user = insert(:user)
2607 user2 = insert(:user)
2608
2609 assert {:ok, user_updated} = user |> User.add_alias(user2)
2610
2611 assert user_updated.also_known_as |> length() == 1
2612 assert user2.ap_id in user_updated.also_known_as
2613 end
2614
2615 test "should add multiple aliases" do
2616 user = insert(:user)
2617 user2 = insert(:user)
2618 user3 = insert(:user)
2619
2620 assert {:ok, user} = user |> User.add_alias(user2)
2621 assert {:ok, user_updated} = user |> User.add_alias(user3)
2622
2623 assert user_updated.also_known_as |> length() == 2
2624 assert user2.ap_id in user_updated.also_known_as
2625 assert user3.ap_id in user_updated.also_known_as
2626 end
2627
2628 test "should not add duplicate aliases" do
2629 user = insert(:user)
2630 user2 = insert(:user)
2631
2632 assert {:ok, user} = user |> User.add_alias(user2)
2633
2634 assert {:ok, user_updated} = user |> User.add_alias(user2)
2635
2636 assert user_updated.also_known_as |> length() == 1
2637 assert user2.ap_id in user_updated.also_known_as
2638 end
2639 end
2640
2641 describe "alias_users/1" do
2642 test "should get aliases for a user" do
2643 user = insert(:user)
2644 user2 = insert(:user, also_known_as: [user.ap_id])
2645
2646 aliases = user2 |> User.alias_users()
2647
2648 assert aliases |> length() == 1
2649
2650 alias_user = aliases |> Enum.at(0)
2651
2652 assert alias_user.ap_id == user.ap_id
2653 end
2654 end
2655
2656 describe "delete_alias/2" do
2657 test "should delete existing alias" do
2658 user = insert(:user)
2659 user2 = insert(:user, also_known_as: [user.ap_id])
2660
2661 assert {:ok, user_updated} = user2 |> User.delete_alias(user)
2662
2663 assert user_updated.also_known_as == []
2664 end
2665
2666 test "should report error on non-existing alias" do
2667 user = insert(:user)
2668 user2 = insert(:user)
2669 user3 = insert(:user, also_known_as: [user.ap_id])
2670
2671 assert {:error, :no_such_alias} = user3 |> User.delete_alias(user2)
2672
2673 user3_updated = User.get_cached_by_ap_id(user3.ap_id)
2674
2675 assert user3_updated.also_known_as |> length() == 1
2676 assert user.ap_id in user3_updated.also_known_as
2677 end
2678 end
2679
2680 describe "follow_hashtag/2" do
2681 test "should follow a hashtag" do
2682 user = insert(:user)
2683 hashtag = insert(:hashtag)
2684
2685 assert {:ok, _} = user |> User.follow_hashtag(hashtag)
2686
2687 user = User.get_cached_by_ap_id(user.ap_id)
2688
2689 assert user.followed_hashtags |> Enum.count() == 1
2690 assert hashtag.name in Enum.map(user.followed_hashtags, fn %{name: name} -> name end)
2691 end
2692
2693 test "should not follow a hashtag twice" do
2694 user = insert(:user)
2695 hashtag = insert(:hashtag)
2696
2697 assert {:ok, _} = user |> User.follow_hashtag(hashtag)
2698
2699 assert {:ok, _} = user |> User.follow_hashtag(hashtag)
2700
2701 user = User.get_cached_by_ap_id(user.ap_id)
2702
2703 assert user.followed_hashtags |> Enum.count() == 1
2704 assert hashtag.name in Enum.map(user.followed_hashtags, fn %{name: name} -> name end)
2705 end
2706
2707 test "can follow multiple hashtags" do
2708 user = insert(:user)
2709 hashtag = insert(:hashtag)
2710 other_hashtag = insert(:hashtag)
2711
2712 assert {:ok, _} = user |> User.follow_hashtag(hashtag)
2713 assert {:ok, _} = user |> User.follow_hashtag(other_hashtag)
2714
2715 user = User.get_cached_by_ap_id(user.ap_id)
2716
2717 assert user.followed_hashtags |> Enum.count() == 2
2718 assert hashtag.name in Enum.map(user.followed_hashtags, fn %{name: name} -> name end)
2719 assert other_hashtag.name in Enum.map(user.followed_hashtags, fn %{name: name} -> name end)
2720 end
2721 end
2722
2723 describe "unfollow_hashtag/2" do
2724 test "should unfollow a hashtag" do
2725 user = insert(:user)
2726 hashtag = insert(:hashtag)
2727
2728 assert {:ok, _} = user |> User.follow_hashtag(hashtag)
2729 assert {:ok, _} = user |> User.unfollow_hashtag(hashtag)
2730
2731 user = User.get_cached_by_ap_id(user.ap_id)
2732
2733 assert user.followed_hashtags |> Enum.count() == 0
2734 end
2735
2736 test "should not error when trying to unfollow a hashtag twice" do
2737 user = insert(:user)
2738 hashtag = insert(:hashtag)
2739
2740 assert {:ok, _} = user |> User.follow_hashtag(hashtag)
2741 assert {:ok, _} = user |> User.unfollow_hashtag(hashtag)
2742 assert {:ok, _} = user |> User.unfollow_hashtag(hashtag)
2743
2744 user = User.get_cached_by_ap_id(user.ap_id)
2745
2746 assert user.followed_hashtags |> Enum.count() == 0
2747 end
2748 end
2749 end