f11aec1364f2ae1d2256a39e99eca62f6c9e92fa
[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(:mrf_simple,
448 media_removal: [],
449 media_nsfw: [],
450 federated_timeline_removal: [],
451 report_removal: [],
452 reject: [],
453 followers_only: [],
454 accept: [],
455 avatar_removal: [],
456 banner_removal: [],
457 reject_deletes: []
458 )
459
460 setup do:
461 clear_config(:mrf,
462 policies: [
463 Pleroma.Web.ActivityPub.MRF.SimplePolicy
464 ]
465 )
466
467 test "it sends a welcome email message if it is set" do
468 welcome_user = insert(:user)
469 clear_config([:welcome, :email, :enabled], true)
470 clear_config([:welcome, :email, :sender], welcome_user.email)
471
472 clear_config(
473 [:welcome, :email, :subject],
474 "Hello, welcome to cool site: <%= instance_name %>"
475 )
476
477 instance_name = Pleroma.Config.get([:instance, :name])
478
479 cng = User.register_changeset(%User{}, @full_user_data)
480 {:ok, registered_user} = User.register(cng)
481 ObanHelpers.perform_all()
482
483 assert_email_sent(
484 from: {instance_name, welcome_user.email},
485 to: {registered_user.name, registered_user.email},
486 subject: "Hello, welcome to cool site: #{instance_name}",
487 html_body: "Welcome to #{instance_name}"
488 )
489 end
490
491 test "it sends a confirm email" do
492 clear_config([:instance, :account_activation_required], true)
493
494 cng = User.register_changeset(%User{}, @full_user_data)
495 {:ok, registered_user} = User.register(cng)
496 ObanHelpers.perform_all()
497
498 Pleroma.Emails.UserEmail.account_confirmation_email(registered_user)
499 # temporary hackney fix until hackney max_connections bug is fixed
500 # https://git.pleroma.social/pleroma/pleroma/-/issues/2101
501 |> Swoosh.Email.put_private(:hackney_options, ssl_options: [versions: [:"tlsv1.2"]])
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 end
969
970 test "returns an ap_id for a user" do
971 user = insert(:user)
972
973 assert User.ap_id(user) ==
974 Pleroma.Web.Router.Helpers.user_feed_url(
975 Pleroma.Web.Endpoint,
976 :feed_redirect,
977 user.nickname
978 )
979 end
980
981 test "returns an ap_followers link for a user" do
982 user = insert(:user)
983
984 assert User.ap_followers(user) ==
985 Pleroma.Web.Router.Helpers.user_feed_url(
986 Pleroma.Web.Endpoint,
987 :feed_redirect,
988 user.nickname
989 ) <> "/followers"
990 end
991
992 describe "remote user changeset" do
993 @valid_remote %{
994 bio: "hello",
995 name: "Someone",
996 nickname: "a@b.de",
997 ap_id: "http...",
998 avatar: %{some: "avatar"}
999 }
1000 setup do: clear_config([:instance, :user_bio_length])
1001 setup do: clear_config([:instance, :user_name_length])
1002
1003 test "it confirms validity" do
1004 cs = User.remote_user_changeset(@valid_remote)
1005 assert cs.valid?
1006 end
1007
1008 test "it sets the follower_adress" do
1009 cs = User.remote_user_changeset(@valid_remote)
1010 # remote users get a fake local follower address
1011 assert cs.changes.follower_address ==
1012 User.ap_followers(%User{nickname: @valid_remote[:nickname]})
1013 end
1014
1015 test "it enforces the fqn format for nicknames" do
1016 cs = User.remote_user_changeset(%{@valid_remote | nickname: "bla"})
1017 assert Ecto.Changeset.get_field(cs, :local) == false
1018 assert cs.changes.avatar
1019 refute cs.valid?
1020 end
1021
1022 test "it has required fields" do
1023 [:ap_id]
1024 |> Enum.each(fn field ->
1025 cs = User.remote_user_changeset(Map.delete(@valid_remote, field))
1026 refute cs.valid?
1027 end)
1028 end
1029
1030 test "it is invalid given a local user" do
1031 user = insert(:user)
1032 cs = User.remote_user_changeset(user, %{name: "tom from myspace"})
1033
1034 refute cs.valid?
1035 end
1036 end
1037
1038 describe "followers and friends" do
1039 test "gets all followers for a given user" do
1040 user = insert(:user)
1041 follower_one = insert(:user)
1042 follower_two = insert(:user)
1043 not_follower = insert(:user)
1044
1045 {:ok, follower_one, user} = User.follow(follower_one, user)
1046 {:ok, follower_two, user} = User.follow(follower_two, user)
1047
1048 res = User.get_followers(user)
1049
1050 assert Enum.member?(res, follower_one)
1051 assert Enum.member?(res, follower_two)
1052 refute Enum.member?(res, not_follower)
1053 end
1054
1055 test "gets all friends (followed users) for a given user" do
1056 user = insert(:user)
1057 followed_one = insert(:user)
1058 followed_two = insert(:user)
1059 not_followed = insert(:user)
1060
1061 {:ok, user, followed_one} = User.follow(user, followed_one)
1062 {:ok, user, followed_two} = User.follow(user, followed_two)
1063
1064 res = User.get_friends(user)
1065
1066 followed_one = User.get_cached_by_ap_id(followed_one.ap_id)
1067 followed_two = User.get_cached_by_ap_id(followed_two.ap_id)
1068 assert Enum.member?(res, followed_one)
1069 assert Enum.member?(res, followed_two)
1070 refute Enum.member?(res, not_followed)
1071 end
1072 end
1073
1074 describe "updating note and follower count" do
1075 test "it sets the note_count property" do
1076 note = insert(:note)
1077
1078 user = User.get_cached_by_ap_id(note.data["actor"])
1079
1080 assert user.note_count == 0
1081
1082 {:ok, user} = User.update_note_count(user)
1083
1084 assert user.note_count == 1
1085 end
1086
1087 test "it increases the note_count property" do
1088 note = insert(:note)
1089 user = User.get_cached_by_ap_id(note.data["actor"])
1090
1091 assert user.note_count == 0
1092
1093 {:ok, user} = User.increase_note_count(user)
1094
1095 assert user.note_count == 1
1096
1097 {:ok, user} = User.increase_note_count(user)
1098
1099 assert user.note_count == 2
1100 end
1101
1102 test "it decreases the note_count property" do
1103 note = insert(:note)
1104 user = User.get_cached_by_ap_id(note.data["actor"])
1105
1106 assert user.note_count == 0
1107
1108 {:ok, user} = User.increase_note_count(user)
1109
1110 assert user.note_count == 1
1111
1112 {:ok, user} = User.decrease_note_count(user)
1113
1114 assert user.note_count == 0
1115
1116 {:ok, user} = User.decrease_note_count(user)
1117
1118 assert user.note_count == 0
1119 end
1120
1121 test "it sets the follower_count property" do
1122 user = insert(:user)
1123 follower = insert(:user)
1124
1125 User.follow(follower, user)
1126
1127 assert user.follower_count == 0
1128
1129 {:ok, user} = User.update_follower_count(user)
1130
1131 assert user.follower_count == 1
1132 end
1133 end
1134
1135 describe "mutes" do
1136 test "it mutes people" do
1137 user = insert(:user)
1138 muted_user = insert(:user)
1139
1140 refute User.mutes?(user, muted_user)
1141 refute User.muted_notifications?(user, muted_user)
1142
1143 {:ok, _user_relationships} = User.mute(user, muted_user)
1144
1145 assert User.mutes?(user, muted_user)
1146 assert User.muted_notifications?(user, muted_user)
1147 end
1148
1149 test "expiring" do
1150 user = insert(:user)
1151 muted_user = insert(:user)
1152
1153 {:ok, _user_relationships} = User.mute(user, muted_user, %{expires_in: 60})
1154 assert User.mutes?(user, muted_user)
1155
1156 worker = Pleroma.Workers.MuteExpireWorker
1157 args = %{"op" => "unmute_user", "muter_id" => user.id, "mutee_id" => muted_user.id}
1158
1159 assert_enqueued(
1160 worker: worker,
1161 args: args
1162 )
1163
1164 assert :ok = perform_job(worker, args)
1165
1166 refute User.mutes?(user, muted_user)
1167 refute User.muted_notifications?(user, muted_user)
1168 end
1169
1170 test "it unmutes users" do
1171 user = insert(:user)
1172 muted_user = insert(:user)
1173
1174 {:ok, _user_relationships} = User.mute(user, muted_user)
1175 {:ok, _user_mute} = User.unmute(user, muted_user)
1176
1177 refute User.mutes?(user, muted_user)
1178 refute User.muted_notifications?(user, muted_user)
1179 end
1180
1181 test "it unmutes users by id" do
1182 user = insert(:user)
1183 muted_user = insert(:user)
1184
1185 {:ok, _user_relationships} = User.mute(user, muted_user)
1186 {:ok, _user_mute} = User.unmute(user.id, muted_user.id)
1187
1188 refute User.mutes?(user, muted_user)
1189 refute User.muted_notifications?(user, muted_user)
1190 end
1191
1192 test "it mutes user without notifications" do
1193 user = insert(:user)
1194 muted_user = insert(:user)
1195
1196 refute User.mutes?(user, muted_user)
1197 refute User.muted_notifications?(user, muted_user)
1198
1199 {:ok, _user_relationships} = User.mute(user, muted_user, %{notifications: false})
1200
1201 assert User.mutes?(user, muted_user)
1202 refute User.muted_notifications?(user, muted_user)
1203 end
1204 end
1205
1206 describe "blocks" do
1207 test "it blocks people" do
1208 user = insert(:user)
1209 blocked_user = insert(:user)
1210
1211 refute User.blocks?(user, blocked_user)
1212
1213 {:ok, _user_relationship} = User.block(user, blocked_user)
1214
1215 assert User.blocks?(user, blocked_user)
1216 end
1217
1218 test "it unblocks users" do
1219 user = insert(:user)
1220 blocked_user = insert(:user)
1221
1222 {:ok, _user_relationship} = User.block(user, blocked_user)
1223 {:ok, _user_block} = User.unblock(user, blocked_user)
1224
1225 refute User.blocks?(user, blocked_user)
1226 end
1227
1228 test "blocks tear down cyclical follow relationships" do
1229 blocker = insert(:user)
1230 blocked = insert(:user)
1231
1232 {:ok, blocker, blocked} = User.follow(blocker, blocked)
1233 {:ok, blocked, blocker} = User.follow(blocked, blocker)
1234
1235 assert User.following?(blocker, blocked)
1236 assert User.following?(blocked, blocker)
1237
1238 {:ok, _user_relationship} = User.block(blocker, blocked)
1239 blocked = User.get_cached_by_id(blocked.id)
1240
1241 assert User.blocks?(blocker, blocked)
1242
1243 refute User.following?(blocker, blocked)
1244 refute User.following?(blocked, blocker)
1245 end
1246
1247 test "blocks tear down blocker->blocked follow relationships" do
1248 blocker = insert(:user)
1249 blocked = insert(:user)
1250
1251 {:ok, blocker, blocked} = User.follow(blocker, blocked)
1252
1253 assert User.following?(blocker, blocked)
1254 refute User.following?(blocked, blocker)
1255
1256 {:ok, _user_relationship} = User.block(blocker, blocked)
1257 blocked = User.get_cached_by_id(blocked.id)
1258
1259 assert User.blocks?(blocker, blocked)
1260
1261 refute User.following?(blocker, blocked)
1262 refute User.following?(blocked, blocker)
1263 end
1264
1265 test "blocks tear down blocked->blocker follow relationships" do
1266 blocker = insert(:user)
1267 blocked = insert(:user)
1268
1269 {:ok, blocked, blocker} = User.follow(blocked, blocker)
1270
1271 refute User.following?(blocker, blocked)
1272 assert User.following?(blocked, blocker)
1273
1274 {:ok, _user_relationship} = User.block(blocker, blocked)
1275 blocked = User.get_cached_by_id(blocked.id)
1276
1277 assert User.blocks?(blocker, blocked)
1278
1279 refute User.following?(blocker, blocked)
1280 refute User.following?(blocked, blocker)
1281 end
1282
1283 test "blocks tear down blocked->blocker subscription relationships" do
1284 blocker = insert(:user)
1285 blocked = insert(:user)
1286
1287 {:ok, _subscription} = User.subscribe(blocked, blocker)
1288
1289 assert User.subscribed_to?(blocked, blocker)
1290 refute User.subscribed_to?(blocker, blocked)
1291
1292 {:ok, _user_relationship} = User.block(blocker, blocked)
1293
1294 assert User.blocks?(blocker, blocked)
1295 refute User.subscribed_to?(blocker, blocked)
1296 refute User.subscribed_to?(blocked, blocker)
1297 end
1298 end
1299
1300 describe "domain blocking" do
1301 test "blocks domains" do
1302 user = insert(:user)
1303 collateral_user = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
1304
1305 {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
1306
1307 assert User.blocks?(user, collateral_user)
1308 end
1309
1310 test "does not block domain with same end" do
1311 user = insert(:user)
1312
1313 collateral_user =
1314 insert(:user, %{ap_id: "https://another-awful-and-rude-instance.com/user/bully"})
1315
1316 {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
1317
1318 refute User.blocks?(user, collateral_user)
1319 end
1320
1321 test "does not block domain with same end if wildcard added" do
1322 user = insert(:user)
1323
1324 collateral_user =
1325 insert(:user, %{ap_id: "https://another-awful-and-rude-instance.com/user/bully"})
1326
1327 {:ok, user} = User.block_domain(user, "*.awful-and-rude-instance.com")
1328
1329 refute User.blocks?(user, collateral_user)
1330 end
1331
1332 test "blocks domain with wildcard for subdomain" do
1333 user = insert(:user)
1334
1335 user_from_subdomain =
1336 insert(:user, %{ap_id: "https://subdomain.awful-and-rude-instance.com/user/bully"})
1337
1338 user_with_two_subdomains =
1339 insert(:user, %{
1340 ap_id: "https://subdomain.second_subdomain.awful-and-rude-instance.com/user/bully"
1341 })
1342
1343 user_domain = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
1344
1345 {:ok, user} = User.block_domain(user, "*.awful-and-rude-instance.com")
1346
1347 assert User.blocks?(user, user_from_subdomain)
1348 assert User.blocks?(user, user_with_two_subdomains)
1349 assert User.blocks?(user, user_domain)
1350 end
1351
1352 test "unblocks domains" do
1353 user = insert(:user)
1354 collateral_user = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
1355
1356 {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
1357 {:ok, user} = User.unblock_domain(user, "awful-and-rude-instance.com")
1358
1359 refute User.blocks?(user, collateral_user)
1360 end
1361
1362 test "follows take precedence over domain blocks" do
1363 user = insert(:user)
1364 good_eggo = insert(:user, %{ap_id: "https://meanies.social/user/cuteposter"})
1365
1366 {:ok, user} = User.block_domain(user, "meanies.social")
1367 {:ok, user, good_eggo} = User.follow(user, good_eggo)
1368
1369 refute User.blocks?(user, good_eggo)
1370 end
1371 end
1372
1373 describe "get_recipients_from_activity" do
1374 test "works for announces" do
1375 actor = insert(:user)
1376 user = insert(:user, local: true)
1377
1378 {:ok, activity} = CommonAPI.post(actor, %{status: "hello"})
1379 {:ok, announce} = CommonAPI.repeat(activity.id, user)
1380
1381 recipients = User.get_recipients_from_activity(announce)
1382
1383 assert user in recipients
1384 end
1385
1386 test "get recipients" do
1387 actor = insert(:user)
1388 user = insert(:user, local: true)
1389 user_two = insert(:user, local: false)
1390 addressed = insert(:user, local: true)
1391 addressed_remote = insert(:user, local: false)
1392
1393 {:ok, activity} =
1394 CommonAPI.post(actor, %{
1395 status: "hey @#{addressed.nickname} @#{addressed_remote.nickname}"
1396 })
1397
1398 assert Enum.map([actor, addressed], & &1.ap_id) --
1399 Enum.map(User.get_recipients_from_activity(activity), & &1.ap_id) == []
1400
1401 {:ok, user, actor} = User.follow(user, actor)
1402 {:ok, _user_two, _actor} = User.follow(user_two, actor)
1403 recipients = User.get_recipients_from_activity(activity)
1404 assert length(recipients) == 3
1405 assert user in recipients
1406 assert addressed in recipients
1407 end
1408
1409 test "has following" do
1410 actor = insert(:user)
1411 user = insert(:user)
1412 user_two = insert(:user)
1413 addressed = insert(:user, local: true)
1414
1415 {:ok, activity} =
1416 CommonAPI.post(actor, %{
1417 status: "hey @#{addressed.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, _actor, _user} = User.follow(actor, user)
1424 {:ok, _actor, _user_two} = User.follow(actor, user_two)
1425 recipients = User.get_recipients_from_activity(activity)
1426 assert length(recipients) == 2
1427 assert addressed in recipients
1428 end
1429 end
1430
1431 describe ".set_activation" do
1432 test "can de-activate then re-activate a user" do
1433 user = insert(:user)
1434 assert user.is_active
1435 {:ok, user} = User.set_activation(user, false)
1436 refute user.is_active
1437 {:ok, user} = User.set_activation(user, true)
1438 assert user.is_active
1439 end
1440
1441 test "hide a user from followers" do
1442 user = insert(:user)
1443 user2 = insert(:user)
1444
1445 {:ok, user, user2} = User.follow(user, user2)
1446 {:ok, _user} = User.set_activation(user, false)
1447
1448 user2 = User.get_cached_by_id(user2.id)
1449
1450 assert user2.follower_count == 0
1451 assert [] = User.get_followers(user2)
1452 end
1453
1454 test "hide a user from friends" do
1455 user = insert(:user)
1456 user2 = insert(:user)
1457
1458 {:ok, user2, user} = User.follow(user2, user)
1459 assert user2.following_count == 1
1460 assert User.following_count(user2) == 1
1461
1462 {:ok, _user} = User.set_activation(user, false)
1463
1464 user2 = User.get_cached_by_id(user2.id)
1465
1466 assert refresh_record(user2).following_count == 0
1467 assert user2.following_count == 0
1468 assert User.following_count(user2) == 0
1469 assert [] = User.get_friends(user2)
1470 end
1471
1472 test "hide a user's statuses from timelines and notifications" do
1473 user = insert(:user)
1474 user2 = insert(:user)
1475
1476 {:ok, user2, user} = User.follow(user2, user)
1477
1478 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{user2.nickname}"})
1479
1480 activity = Repo.preload(activity, :bookmark)
1481
1482 [notification] = Pleroma.Notification.for_user(user2)
1483 assert notification.activity.id == activity.id
1484
1485 assert [activity] == ActivityPub.fetch_public_activities(%{}) |> Repo.preload(:bookmark)
1486
1487 assert [%{activity | thread_muted?: CommonAPI.thread_muted?(user2, activity)}] ==
1488 ActivityPub.fetch_activities([user2.ap_id | User.following(user2)], %{
1489 user: user2
1490 })
1491
1492 {:ok, _user} = User.set_activation(user, false)
1493
1494 assert [] == ActivityPub.fetch_public_activities(%{})
1495 assert [] == Pleroma.Notification.for_user(user2)
1496
1497 assert [] ==
1498 ActivityPub.fetch_activities([user2.ap_id | User.following(user2)], %{
1499 user: user2
1500 })
1501 end
1502 end
1503
1504 describe "approve" do
1505 test "approves a user" do
1506 user = insert(:user, is_approved: false)
1507 refute user.is_approved
1508 {:ok, user} = User.approve(user)
1509 assert user.is_approved
1510 end
1511
1512 test "approves a list of users" do
1513 unapproved_users = [
1514 insert(:user, is_approved: false),
1515 insert(:user, is_approved: false),
1516 insert(:user, is_approved: false)
1517 ]
1518
1519 {:ok, users} = User.approve(unapproved_users)
1520
1521 assert Enum.count(users) == 3
1522
1523 Enum.each(users, fn user ->
1524 assert user.is_approved
1525 end)
1526 end
1527
1528 test "it sends welcome email if it is set" do
1529 clear_config([:welcome, :email, :enabled], true)
1530 clear_config([:welcome, :email, :sender], "tester@test.me")
1531
1532 user = insert(:user, is_approved: false)
1533 welcome_user = insert(:user, email: "tester@test.me")
1534 instance_name = Pleroma.Config.get([:instance, :name])
1535
1536 User.approve(user)
1537
1538 ObanHelpers.perform_all()
1539
1540 assert_email_sent(
1541 from: {instance_name, welcome_user.email},
1542 to: {user.name, user.email},
1543 html_body: "Welcome to #{instance_name}"
1544 )
1545 end
1546
1547 test "approving an approved user does not trigger post-register actions" do
1548 clear_config([:welcome, :email, :enabled], true)
1549
1550 user = insert(:user, is_approved: true)
1551 User.approve(user)
1552
1553 ObanHelpers.perform_all()
1554
1555 assert_no_email_sent()
1556 end
1557 end
1558
1559 describe "confirm" do
1560 test "confirms a user" do
1561 user = insert(:user, is_confirmed: false)
1562 refute user.is_confirmed
1563 {:ok, user} = User.confirm(user)
1564 assert user.is_confirmed
1565 end
1566
1567 test "confirms a list of users" do
1568 unconfirmed_users = [
1569 insert(:user, is_confirmed: false),
1570 insert(:user, is_confirmed: false),
1571 insert(:user, is_confirmed: false)
1572 ]
1573
1574 {:ok, users} = User.confirm(unconfirmed_users)
1575
1576 assert Enum.count(users) == 3
1577
1578 Enum.each(users, fn user ->
1579 assert user.is_confirmed
1580 end)
1581 end
1582
1583 test "sends approval emails when `is_approved: false`" do
1584 admin = insert(:user, is_admin: true)
1585 user = insert(:user, is_confirmed: false, is_approved: false)
1586 User.confirm(user)
1587
1588 ObanHelpers.perform_all()
1589
1590 user_email = Pleroma.Emails.UserEmail.approval_pending_email(user)
1591 admin_email = Pleroma.Emails.AdminEmail.new_unapproved_registration(admin, user)
1592
1593 notify_email = Pleroma.Config.get([:instance, :notify_email])
1594 instance_name = Pleroma.Config.get([:instance, :name])
1595
1596 # User approval email
1597 assert_email_sent(
1598 from: {instance_name, notify_email},
1599 to: {user.name, user.email},
1600 html_body: user_email.html_body
1601 )
1602
1603 # Admin email
1604 assert_email_sent(
1605 from: {instance_name, notify_email},
1606 to: {admin.name, admin.email},
1607 html_body: admin_email.html_body
1608 )
1609 end
1610
1611 test "confirming a confirmed user does not trigger post-register actions" do
1612 user = insert(:user, is_confirmed: true, is_approved: false)
1613 User.confirm(user)
1614
1615 ObanHelpers.perform_all()
1616
1617 assert_no_email_sent()
1618 end
1619 end
1620
1621 describe "delete" do
1622 setup do
1623 {:ok, user} = insert(:user) |> User.set_cache()
1624
1625 [user: user]
1626 end
1627
1628 setup do: clear_config([:instance, :federating])
1629
1630 test ".delete_user_activities deletes all create activities", %{user: user} do
1631 {:ok, activity} = CommonAPI.post(user, %{status: "2hu"})
1632
1633 User.delete_user_activities(user)
1634
1635 # TODO: Test removal favorites, repeats, delete activities.
1636 refute Activity.get_by_id(activity.id)
1637 end
1638
1639 test "it deactivates a user, all follow relationships and all activities", %{user: user} do
1640 follower = insert(:user)
1641 {:ok, follower, user} = User.follow(follower, user)
1642
1643 locked_user = insert(:user, name: "locked", is_locked: true)
1644 {:ok, _, _} = User.follow(user, locked_user, :follow_pending)
1645
1646 object = insert(:note, user: user)
1647 activity = insert(:note_activity, user: user, note: object)
1648
1649 object_two = insert(:note, user: follower)
1650 activity_two = insert(:note_activity, user: follower, note: object_two)
1651
1652 {:ok, like} = CommonAPI.favorite(user, activity_two.id)
1653 {:ok, like_two} = CommonAPI.favorite(follower, activity.id)
1654 {:ok, repeat} = CommonAPI.repeat(activity_two.id, user)
1655
1656 {:ok, job} = User.delete(user)
1657 {:ok, _user} = ObanHelpers.perform(job)
1658
1659 follower = User.get_cached_by_id(follower.id)
1660
1661 refute User.following?(follower, user)
1662 assert %{is_active: false} = User.get_by_id(user.id)
1663
1664 assert [] == User.get_follow_requests(locked_user)
1665
1666 user_activities =
1667 user.ap_id
1668 |> Activity.Queries.by_actor()
1669 |> Repo.all()
1670 |> Enum.map(fn act -> act.data["type"] end)
1671
1672 assert Enum.all?(user_activities, fn act -> act in ~w(Delete Undo) end)
1673
1674 refute Activity.get_by_id(activity.id)
1675 refute Activity.get_by_id(like.id)
1676 refute Activity.get_by_id(like_two.id)
1677 refute Activity.get_by_id(repeat.id)
1678 end
1679 end
1680
1681 test "delete/1 when confirmation is pending deletes the user" do
1682 clear_config([:instance, :account_activation_required], true)
1683 user = insert(:user, is_confirmed: false)
1684
1685 {:ok, job} = User.delete(user)
1686 {:ok, _} = ObanHelpers.perform(job)
1687
1688 refute User.get_cached_by_id(user.id)
1689 refute User.get_by_id(user.id)
1690 end
1691
1692 test "delete/1 when approval is pending deletes the user" do
1693 user = insert(:user, is_approved: false)
1694
1695 {:ok, job} = User.delete(user)
1696 {:ok, _} = ObanHelpers.perform(job)
1697
1698 refute User.get_cached_by_id(user.id)
1699 refute User.get_by_id(user.id)
1700 end
1701
1702 test "delete/1 purges a user when they wouldn't be fully deleted" do
1703 user =
1704 insert(:user, %{
1705 bio: "eyy lmao",
1706 name: "qqqqqqq",
1707 password_hash: "pdfk2$1b3n159001",
1708 keys: "RSA begin buplic key",
1709 public_key: "--PRIVATE KEYE--",
1710 avatar: %{"a" => "b"},
1711 tags: ["qqqqq"],
1712 banner: %{"a" => "b"},
1713 background: %{"a" => "b"},
1714 note_count: 9,
1715 follower_count: 9,
1716 following_count: 9001,
1717 is_locked: true,
1718 is_confirmed: true,
1719 password_reset_pending: true,
1720 is_approved: true,
1721 registration_reason: "ahhhhh",
1722 confirmation_token: "qqqq",
1723 domain_blocks: ["lain.com"],
1724 is_active: false,
1725 ap_enabled: true,
1726 is_moderator: true,
1727 is_admin: true,
1728 mastofe_settings: %{"a" => "b"},
1729 mascot: %{"a" => "b"},
1730 emoji: %{"a" => "b"},
1731 pleroma_settings_store: %{"q" => "x"},
1732 fields: [%{"gg" => "qq"}],
1733 raw_fields: [%{"gg" => "qq"}],
1734 is_discoverable: true,
1735 also_known_as: ["https://lol.olo/users/loll"]
1736 })
1737
1738 {:ok, job} = User.delete(user)
1739 {:ok, _} = ObanHelpers.perform(job)
1740 user = User.get_by_id(user.id)
1741
1742 assert %User{
1743 bio: "",
1744 raw_bio: nil,
1745 email: nil,
1746 name: nil,
1747 password_hash: nil,
1748 keys: "RSA begin buplic key",
1749 public_key: "--PRIVATE KEYE--",
1750 avatar: %{},
1751 tags: [],
1752 last_refreshed_at: nil,
1753 last_digest_emailed_at: nil,
1754 banner: %{},
1755 background: %{},
1756 note_count: 0,
1757 follower_count: 0,
1758 following_count: 0,
1759 is_locked: false,
1760 is_confirmed: true,
1761 password_reset_pending: false,
1762 is_approved: true,
1763 registration_reason: nil,
1764 confirmation_token: nil,
1765 domain_blocks: [],
1766 is_active: false,
1767 ap_enabled: false,
1768 is_moderator: false,
1769 is_admin: false,
1770 mastofe_settings: nil,
1771 mascot: nil,
1772 emoji: %{},
1773 pleroma_settings_store: %{},
1774 fields: [],
1775 raw_fields: [],
1776 is_discoverable: false,
1777 also_known_as: []
1778 } = user
1779 end
1780
1781 test "delete/1 purges a remote user" do
1782 user =
1783 insert(:user, %{
1784 name: "qqqqqqq",
1785 avatar: %{"a" => "b"},
1786 banner: %{"a" => "b"},
1787 local: false
1788 })
1789
1790 {:ok, job} = User.delete(user)
1791 {:ok, _} = ObanHelpers.perform(job)
1792 user = User.get_by_id(user.id)
1793
1794 assert user.name == nil
1795 assert user.avatar == %{}
1796 assert user.banner == %{}
1797 end
1798
1799 describe "set_suggestion" do
1800 test "suggests a user" do
1801 user = insert(:user, is_suggested: false)
1802 refute user.is_suggested
1803 {:ok, user} = User.set_suggestion(user, true)
1804 assert user.is_suggested
1805 end
1806
1807 test "suggests a list of users" do
1808 unsuggested_users = [
1809 insert(:user, is_suggested: false),
1810 insert(:user, is_suggested: false),
1811 insert(:user, is_suggested: false)
1812 ]
1813
1814 {:ok, users} = User.set_suggestion(unsuggested_users, true)
1815
1816 assert Enum.count(users) == 3
1817
1818 Enum.each(users, fn user ->
1819 assert user.is_suggested
1820 end)
1821 end
1822
1823 test "unsuggests a user" do
1824 user = insert(:user, is_suggested: true)
1825 assert user.is_suggested
1826 {:ok, user} = User.set_suggestion(user, false)
1827 refute user.is_suggested
1828 end
1829 end
1830
1831 test "get_public_key_for_ap_id fetches a user that's not in the db" do
1832 assert {:ok, _key} = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin")
1833 end
1834
1835 describe "per-user rich-text filtering" do
1836 test "html_filter_policy returns default policies, when rich-text is enabled" do
1837 user = insert(:user)
1838
1839 assert Pleroma.Config.get([:markup, :scrub_policy]) == User.html_filter_policy(user)
1840 end
1841
1842 test "html_filter_policy returns TwitterText scrubber when rich-text is disabled" do
1843 user = insert(:user, no_rich_text: true)
1844
1845 assert Pleroma.HTML.Scrubber.TwitterText == User.html_filter_policy(user)
1846 end
1847 end
1848
1849 describe "caching" do
1850 test "invalidate_cache works" do
1851 user = insert(:user)
1852
1853 User.set_cache(user)
1854 User.invalidate_cache(user)
1855
1856 {:ok, nil} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
1857 {:ok, nil} = Cachex.get(:user_cache, "nickname:#{user.nickname}")
1858 end
1859
1860 test "User.delete() plugs any possible zombie objects" do
1861 user = insert(:user)
1862
1863 {:ok, job} = User.delete(user)
1864 {:ok, _} = ObanHelpers.perform(job)
1865
1866 {:ok, cached_user} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
1867
1868 assert cached_user != user
1869
1870 {:ok, cached_user} = Cachex.get(:user_cache, "nickname:#{user.ap_id}")
1871
1872 assert cached_user != user
1873 end
1874 end
1875
1876 describe "account_status/1" do
1877 setup do: clear_config([:instance, :account_activation_required])
1878
1879 test "return confirmation_pending for unconfirm user" do
1880 clear_config([:instance, :account_activation_required], true)
1881 user = insert(:user, is_confirmed: false)
1882 assert User.account_status(user) == :confirmation_pending
1883 end
1884
1885 test "return active for confirmed user" do
1886 clear_config([:instance, :account_activation_required], true)
1887 user = insert(:user, is_confirmed: true)
1888 assert User.account_status(user) == :active
1889 end
1890
1891 test "return active for remote user" do
1892 user = insert(:user, local: false)
1893 assert User.account_status(user) == :active
1894 end
1895
1896 test "returns :password_reset_pending for user with reset password" do
1897 user = insert(:user, password_reset_pending: true)
1898 assert User.account_status(user) == :password_reset_pending
1899 end
1900
1901 test "returns :deactivated for deactivated user" do
1902 user = insert(:user, local: true, is_confirmed: true, is_active: false)
1903 assert User.account_status(user) == :deactivated
1904 end
1905
1906 test "returns :approval_pending for unapproved user" do
1907 user = insert(:user, local: true, is_approved: false)
1908 assert User.account_status(user) == :approval_pending
1909
1910 user = insert(:user, local: true, is_confirmed: false, is_approved: false)
1911 assert User.account_status(user) == :approval_pending
1912 end
1913 end
1914
1915 describe "superuser?/1" do
1916 test "returns false for unprivileged users" do
1917 user = insert(:user, local: true)
1918
1919 refute User.superuser?(user)
1920 end
1921
1922 test "returns false for remote users" do
1923 user = insert(:user, local: false)
1924 remote_admin_user = insert(:user, local: false, is_admin: true)
1925
1926 refute User.superuser?(user)
1927 refute User.superuser?(remote_admin_user)
1928 end
1929
1930 test "returns true for local moderators" do
1931 user = insert(:user, local: true, is_moderator: true)
1932
1933 assert User.superuser?(user)
1934 end
1935
1936 test "returns true for local admins" do
1937 user = insert(:user, local: true, is_admin: true)
1938
1939 assert User.superuser?(user)
1940 end
1941 end
1942
1943 describe "invisible?/1" do
1944 test "returns true for an invisible user" do
1945 user = insert(:user, local: true, invisible: true)
1946
1947 assert User.invisible?(user)
1948 end
1949
1950 test "returns false for a non-invisible user" do
1951 user = insert(:user, local: true)
1952
1953 refute User.invisible?(user)
1954 end
1955 end
1956
1957 describe "visible_for/2" do
1958 test "returns true when the account is itself" do
1959 user = insert(:user, local: true)
1960
1961 assert User.visible_for(user, user) == :visible
1962 end
1963
1964 test "returns false when the account is unconfirmed and confirmation is required" do
1965 clear_config([:instance, :account_activation_required], true)
1966
1967 user = insert(:user, local: true, is_confirmed: false)
1968 other_user = insert(:user, local: true)
1969
1970 refute User.visible_for(user, other_user) == :visible
1971 end
1972
1973 test "returns true when the account is unconfirmed and confirmation is required but the account is remote" do
1974 clear_config([:instance, :account_activation_required], true)
1975
1976 user = insert(:user, local: false, is_confirmed: false)
1977 other_user = insert(:user, local: true)
1978
1979 assert User.visible_for(user, other_user) == :visible
1980 end
1981
1982 test "returns true when the account is unconfirmed and being viewed by a privileged account (confirmation required)" do
1983 clear_config([:instance, :account_activation_required], true)
1984
1985 user = insert(:user, local: true, is_confirmed: false)
1986 other_user = insert(:user, local: true, is_admin: true)
1987
1988 assert User.visible_for(user, other_user) == :visible
1989 end
1990 end
1991
1992 describe "parse_bio/2" do
1993 test "preserves hosts in user links text" do
1994 remote_user = insert(:user, local: false, nickname: "nick@domain.com")
1995 user = insert(:user)
1996 bio = "A.k.a. @nick@domain.com"
1997
1998 expected_text =
1999 ~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>)
2000
2001 assert expected_text == User.parse_bio(bio, user)
2002 end
2003
2004 test "Adds rel=me on linkbacked urls" do
2005 user = insert(:user, ap_id: "https://social.example.org/users/lain")
2006
2007 bio = "http://example.com/rel_me/null"
2008 expected_text = "<a href=\"#{bio}\">#{bio}</a>"
2009 assert expected_text == User.parse_bio(bio, user)
2010
2011 bio = "http://example.com/rel_me/link"
2012 expected_text = "<a href=\"#{bio}\" rel=\"me\">#{bio}</a>"
2013 assert expected_text == User.parse_bio(bio, user)
2014
2015 bio = "http://example.com/rel_me/anchor"
2016 expected_text = "<a href=\"#{bio}\" rel=\"me\">#{bio}</a>"
2017 assert expected_text == User.parse_bio(bio, user)
2018 end
2019 end
2020
2021 test "follower count is updated when a follower is blocked" do
2022 user = insert(:user)
2023 follower = insert(:user)
2024 follower2 = insert(:user)
2025 follower3 = insert(:user)
2026
2027 {:ok, follower, user} = User.follow(follower, user)
2028 {:ok, _follower2, _user} = User.follow(follower2, user)
2029 {:ok, _follower3, _user} = User.follow(follower3, user)
2030
2031 {:ok, _user_relationship} = User.block(user, follower)
2032 user = refresh_record(user)
2033
2034 assert user.follower_count == 2
2035 end
2036
2037 describe "list_inactive_users_query/1" do
2038 defp days_ago(days) do
2039 NaiveDateTime.add(
2040 NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second),
2041 -days * 60 * 60 * 24,
2042 :second
2043 )
2044 end
2045
2046 test "Users are inactive by default" do
2047 total = 10
2048
2049 users =
2050 Enum.map(1..total, fn _ ->
2051 insert(:user, last_digest_emailed_at: days_ago(20), is_active: true)
2052 end)
2053
2054 inactive_users_ids =
2055 Pleroma.User.list_inactive_users_query()
2056 |> Pleroma.Repo.all()
2057 |> Enum.map(& &1.id)
2058
2059 Enum.each(users, fn user ->
2060 assert user.id in inactive_users_ids
2061 end)
2062 end
2063
2064 test "Only includes users who has no recent activity" do
2065 total = 10
2066
2067 users =
2068 Enum.map(1..total, fn _ ->
2069 insert(:user, last_digest_emailed_at: days_ago(20), is_active: true)
2070 end)
2071
2072 {inactive, active} = Enum.split(users, trunc(total / 2))
2073
2074 Enum.map(active, fn user ->
2075 to = Enum.random(users -- [user])
2076
2077 {:ok, _} =
2078 CommonAPI.post(user, %{
2079 status: "hey @#{to.nickname}"
2080 })
2081 end)
2082
2083 inactive_users_ids =
2084 Pleroma.User.list_inactive_users_query()
2085 |> Pleroma.Repo.all()
2086 |> Enum.map(& &1.id)
2087
2088 Enum.each(active, fn user ->
2089 refute user.id in inactive_users_ids
2090 end)
2091
2092 Enum.each(inactive, fn user ->
2093 assert user.id in inactive_users_ids
2094 end)
2095 end
2096
2097 test "Only includes users with no read notifications" do
2098 total = 10
2099
2100 users =
2101 Enum.map(1..total, fn _ ->
2102 insert(:user, last_digest_emailed_at: days_ago(20), is_active: true)
2103 end)
2104
2105 [sender | recipients] = users
2106 {inactive, active} = Enum.split(recipients, trunc(total / 2))
2107
2108 Enum.each(recipients, fn to ->
2109 {:ok, _} =
2110 CommonAPI.post(sender, %{
2111 status: "hey @#{to.nickname}"
2112 })
2113
2114 {:ok, _} =
2115 CommonAPI.post(sender, %{
2116 status: "hey again @#{to.nickname}"
2117 })
2118 end)
2119
2120 Enum.each(active, fn user ->
2121 [n1, _n2] = Pleroma.Notification.for_user(user)
2122 {:ok, _} = Pleroma.Notification.read_one(user, n1.id)
2123 end)
2124
2125 inactive_users_ids =
2126 Pleroma.User.list_inactive_users_query()
2127 |> Pleroma.Repo.all()
2128 |> Enum.map(& &1.id)
2129
2130 Enum.each(active, fn user ->
2131 refute user.id in inactive_users_ids
2132 end)
2133
2134 Enum.each(inactive, fn user ->
2135 assert user.id in inactive_users_ids
2136 end)
2137 end
2138 end
2139
2140 describe "get_ap_ids_by_nicknames" do
2141 test "it returns a list of AP ids for a given set of nicknames" do
2142 user = insert(:user)
2143 user_two = insert(:user)
2144
2145 ap_ids = User.get_ap_ids_by_nicknames([user.nickname, user_two.nickname, "nonexistent"])
2146 assert length(ap_ids) == 2
2147 assert user.ap_id in ap_ids
2148 assert user_two.ap_id in ap_ids
2149 end
2150 end
2151
2152 describe "sync followers count" do
2153 setup do
2154 user1 = insert(:user, local: false, ap_id: "http://localhost:4001/users/masto_closed")
2155 user2 = insert(:user, local: false, ap_id: "http://localhost:4001/users/fuser2")
2156 insert(:user, local: true)
2157 insert(:user, local: false, is_active: false)
2158 {:ok, user1: user1, user2: user2}
2159 end
2160
2161 test "external_users/1 external active users with limit", %{user1: user1, user2: user2} do
2162 [fdb_user1] = User.external_users(limit: 1)
2163
2164 assert fdb_user1.ap_id
2165 assert fdb_user1.ap_id == user1.ap_id
2166 assert fdb_user1.id == user1.id
2167
2168 [fdb_user2] = User.external_users(max_id: fdb_user1.id, limit: 1)
2169
2170 assert fdb_user2.ap_id
2171 assert fdb_user2.ap_id == user2.ap_id
2172 assert fdb_user2.id == user2.id
2173
2174 assert User.external_users(max_id: fdb_user2.id, limit: 1) == []
2175 end
2176 end
2177
2178 describe "is_internal_user?/1" do
2179 test "non-internal user returns false" do
2180 user = insert(:user)
2181 refute User.is_internal_user?(user)
2182 end
2183
2184 test "user with no nickname returns true" do
2185 user = insert(:user, %{nickname: nil})
2186 assert User.is_internal_user?(user)
2187 end
2188
2189 test "user with internal-prefixed nickname returns true" do
2190 user = insert(:user, %{nickname: "internal.test"})
2191 assert User.is_internal_user?(user)
2192 end
2193 end
2194
2195 describe "update_and_set_cache/1" do
2196 test "returns error when user is stale instead Ecto.StaleEntryError" do
2197 user = insert(:user)
2198
2199 changeset = Ecto.Changeset.change(user, bio: "test")
2200
2201 Repo.delete(user)
2202
2203 assert {:error, %Ecto.Changeset{errors: [id: {"is stale", [stale: true]}], valid?: false}} =
2204 User.update_and_set_cache(changeset)
2205 end
2206
2207 test "performs update cache if user updated" do
2208 user = insert(:user)
2209 assert {:ok, nil} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
2210
2211 changeset = Ecto.Changeset.change(user, bio: "test-bio")
2212
2213 assert {:ok, %User{bio: "test-bio"} = user} = User.update_and_set_cache(changeset)
2214 assert {:ok, user} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
2215 assert %User{bio: "test-bio"} = User.get_cached_by_ap_id(user.ap_id)
2216 end
2217
2218 test "removes report notifs when user isn't superuser any more" do
2219 report_activity = insert(:report_activity)
2220 user = insert(:user, is_moderator: true, is_admin: true)
2221 {:ok, _} = Notification.create_notifications(report_activity)
2222
2223 assert [%Pleroma.Notification{type: "pleroma:report"}] = Notification.for_user(user)
2224
2225 {:ok, user} = user |> User.admin_api_update(%{is_moderator: false})
2226 # is still superuser because still admin
2227 assert [%Pleroma.Notification{type: "pleroma:report"}] = Notification.for_user(user)
2228
2229 {:ok, user} = user |> User.admin_api_update(%{is_moderator: true, is_admin: false})
2230 # is still superuser because still moderator
2231 assert [%Pleroma.Notification{type: "pleroma:report"}] = Notification.for_user(user)
2232
2233 {:ok, user} = user |> User.admin_api_update(%{is_moderator: false})
2234 # is not a superuser any more
2235 assert [] = Notification.for_user(user)
2236 end
2237 end
2238
2239 describe "following/followers synchronization" do
2240 setup do: clear_config([:instance, :external_user_synchronization])
2241
2242 test "updates the counters normally on following/getting a follow when disabled" do
2243 clear_config([:instance, :external_user_synchronization], false)
2244 user = insert(:user)
2245
2246 other_user =
2247 insert(:user,
2248 local: false,
2249 follower_address: "http://localhost:4001/users/masto_closed/followers",
2250 following_address: "http://localhost:4001/users/masto_closed/following",
2251 ap_enabled: true
2252 )
2253
2254 assert other_user.following_count == 0
2255 assert other_user.follower_count == 0
2256
2257 {:ok, user, other_user} = Pleroma.User.follow(user, other_user)
2258
2259 assert user.following_count == 1
2260 assert other_user.follower_count == 1
2261 end
2262
2263 test "synchronizes the counters with the remote instance for the followed when enabled" do
2264 clear_config([:instance, :external_user_synchronization], false)
2265
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 clear_config([:instance, :external_user_synchronization], true)
2280 {:ok, _user, other_user} = User.follow(user, other_user)
2281
2282 assert other_user.follower_count == 437
2283 end
2284
2285 test "synchronizes the counters with the remote instance for the follower 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, other_user, _user} = User.follow(other_user, user)
2303
2304 assert other_user.following_count == 152
2305 end
2306 end
2307
2308 describe "change_email/2" do
2309 setup do
2310 [user: insert(:user)]
2311 end
2312
2313 test "blank email returns error if we require an email on registration", %{user: user} do
2314 orig_account_activation_required =
2315 Pleroma.Config.get([:instance, :account_activation_required])
2316
2317 Pleroma.Config.put([:instance, :account_activation_required], true)
2318
2319 on_exit(fn ->
2320 Pleroma.Config.put(
2321 [:instance, :account_activation_required],
2322 orig_account_activation_required
2323 )
2324 end)
2325
2326 assert {:error, %{errors: [email: {"can't be blank", _}]}} = User.change_email(user, "")
2327 assert {:error, %{errors: [email: {"can't be blank", _}]}} = User.change_email(user, nil)
2328 end
2329
2330 test "blank email should be fine if we do not require an email on registration", %{user: user} do
2331 orig_account_activation_required =
2332 Pleroma.Config.get([:instance, :account_activation_required])
2333
2334 Pleroma.Config.put([:instance, :account_activation_required], false)
2335
2336 on_exit(fn ->
2337 Pleroma.Config.put(
2338 [:instance, :account_activation_required],
2339 orig_account_activation_required
2340 )
2341 end)
2342
2343 assert {:ok, %User{email: nil}} = User.change_email(user, "")
2344 assert {:ok, %User{email: nil}} = User.change_email(user, nil)
2345 end
2346
2347 test "non unique email returns error", %{user: user} do
2348 %{email: email} = insert(:user)
2349
2350 assert {:error, %{errors: [email: {"has already been taken", _}]}} =
2351 User.change_email(user, email)
2352 end
2353
2354 test "invalid email returns error", %{user: user} do
2355 assert {:error, %{errors: [email: {"has invalid format", _}]}} =
2356 User.change_email(user, "cofe")
2357 end
2358
2359 test "changes email", %{user: user} do
2360 assert {:ok, %User{email: "cofe@cofe.party"}} = User.change_email(user, "cofe@cofe.party")
2361 end
2362
2363 test "adds email", %{user: user} do
2364 orig_account_activation_required =
2365 Pleroma.Config.get([:instance, :account_activation_required])
2366
2367 Pleroma.Config.put([:instance, :account_activation_required], false)
2368
2369 on_exit(fn ->
2370 Pleroma.Config.put(
2371 [:instance, :account_activation_required],
2372 orig_account_activation_required
2373 )
2374 end)
2375
2376 assert {:ok, _} = User.change_email(user, "")
2377 Pleroma.Config.put([:instance, :account_activation_required], true)
2378
2379 assert {:ok, %User{email: "cofe2@cofe.party"}} = User.change_email(user, "cofe2@cofe.party")
2380 end
2381 end
2382
2383 describe "get_cached_by_nickname_or_id" do
2384 setup do
2385 local_user = insert(:user)
2386 remote_user = insert(:user, nickname: "nickname@example.com", local: false)
2387
2388 [local_user: local_user, remote_user: remote_user]
2389 end
2390
2391 setup do: clear_config([:instance, :limit_to_local_content])
2392
2393 test "allows getting remote users by id no matter what :limit_to_local_content is set to", %{
2394 remote_user: remote_user
2395 } do
2396 clear_config([:instance, :limit_to_local_content], false)
2397 assert %User{} = User.get_cached_by_nickname_or_id(remote_user.id)
2398
2399 clear_config([:instance, :limit_to_local_content], true)
2400 assert %User{} = User.get_cached_by_nickname_or_id(remote_user.id)
2401
2402 clear_config([:instance, :limit_to_local_content], :unauthenticated)
2403 assert %User{} = User.get_cached_by_nickname_or_id(remote_user.id)
2404 end
2405
2406 test "disallows getting remote users by nickname without authentication when :limit_to_local_content is set to :unauthenticated",
2407 %{remote_user: remote_user} do
2408 clear_config([:instance, :limit_to_local_content], :unauthenticated)
2409 assert nil == User.get_cached_by_nickname_or_id(remote_user.nickname)
2410 end
2411
2412 test "allows getting remote users by nickname with authentication when :limit_to_local_content is set to :unauthenticated",
2413 %{remote_user: remote_user, local_user: local_user} do
2414 clear_config([:instance, :limit_to_local_content], :unauthenticated)
2415 assert %User{} = User.get_cached_by_nickname_or_id(remote_user.nickname, for: local_user)
2416 end
2417
2418 test "disallows getting remote users by nickname when :limit_to_local_content is set to true",
2419 %{remote_user: remote_user} do
2420 clear_config([:instance, :limit_to_local_content], true)
2421 assert nil == User.get_cached_by_nickname_or_id(remote_user.nickname)
2422 end
2423
2424 test "allows getting local users by nickname no matter what :limit_to_local_content is set to",
2425 %{local_user: local_user} do
2426 clear_config([:instance, :limit_to_local_content], false)
2427 assert %User{} = User.get_cached_by_nickname_or_id(local_user.nickname)
2428
2429 clear_config([:instance, :limit_to_local_content], true)
2430 assert %User{} = User.get_cached_by_nickname_or_id(local_user.nickname)
2431
2432 clear_config([:instance, :limit_to_local_content], :unauthenticated)
2433 assert %User{} = User.get_cached_by_nickname_or_id(local_user.nickname)
2434 end
2435 end
2436
2437 describe "update_email_notifications/2" do
2438 setup do
2439 user = insert(:user, email_notifications: %{"digest" => true})
2440
2441 {:ok, user: user}
2442 end
2443
2444 test "Notifications are updated", %{user: user} do
2445 true = user.email_notifications["digest"]
2446 assert {:ok, result} = User.update_email_notifications(user, %{"digest" => false})
2447 assert result.email_notifications["digest"] == false
2448 end
2449 end
2450
2451 describe "local_nickname/1" do
2452 test "returns nickname without host" do
2453 assert User.local_nickname("@mentioned") == "mentioned"
2454 assert User.local_nickname("a_local_nickname") == "a_local_nickname"
2455 assert User.local_nickname("nickname@host.com") == "nickname"
2456 end
2457 end
2458
2459 describe "full_nickname/1" do
2460 test "returns fully qualified nickname for local and remote users" do
2461 local_user =
2462 insert(:user, nickname: "local_user", ap_id: "https://somehost.com/users/local_user")
2463
2464 remote_user = insert(:user, nickname: "remote@host.com", local: false)
2465
2466 assert User.full_nickname(local_user) == "local_user@somehost.com"
2467 assert User.full_nickname(remote_user) == "remote@host.com"
2468 end
2469
2470 test "strips leading @ from mentions" do
2471 assert User.full_nickname("@mentioned") == "mentioned"
2472 assert User.full_nickname("@nickname@host.com") == "nickname@host.com"
2473 end
2474
2475 test "does not modify nicknames" do
2476 assert User.full_nickname("nickname") == "nickname"
2477 assert User.full_nickname("nickname@host.com") == "nickname@host.com"
2478 end
2479 end
2480
2481 test "avatar fallback" do
2482 user = insert(:user)
2483 assert User.avatar_url(user) =~ "/images/avi.png"
2484
2485 clear_config([:assets, :default_user_avatar], "avatar.png")
2486
2487 user = User.get_cached_by_nickname_or_id(user.nickname)
2488 assert User.avatar_url(user) =~ "avatar.png"
2489
2490 assert User.avatar_url(user, no_default: true) == nil
2491 end
2492
2493 test "get_host/1" do
2494 user = insert(:user, ap_id: "https://lain.com/users/lain", nickname: "lain")
2495 assert User.get_host(user) == "lain.com"
2496 end
2497
2498 test "update_last_active_at/1" do
2499 user = insert(:user)
2500 assert is_nil(user.last_active_at)
2501
2502 test_started_at = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
2503
2504 assert {:ok, user} = User.update_last_active_at(user)
2505
2506 assert user.last_active_at >= test_started_at
2507 assert user.last_active_at <= NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
2508
2509 last_active_at =
2510 NaiveDateTime.utc_now()
2511 |> NaiveDateTime.add(-:timer.hours(24), :millisecond)
2512 |> NaiveDateTime.truncate(:second)
2513
2514 assert {:ok, user} =
2515 user
2516 |> cast(%{last_active_at: last_active_at}, [:last_active_at])
2517 |> User.update_and_set_cache()
2518
2519 assert user.last_active_at == last_active_at
2520 assert {:ok, user} = User.update_last_active_at(user)
2521 assert user.last_active_at >= test_started_at
2522 assert user.last_active_at <= NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
2523 end
2524
2525 test "active_user_count/1" do
2526 insert(:user)
2527 insert(:user, %{local: false})
2528 insert(:user, %{last_active_at: NaiveDateTime.utc_now()})
2529 insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), days: -15)})
2530 insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), weeks: -6)})
2531 insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), months: -7)})
2532 insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), years: -2)})
2533
2534 assert User.active_user_count() == 2
2535 assert User.active_user_count(180) == 3
2536 assert User.active_user_count(365) == 4
2537 assert User.active_user_count(1000) == 5
2538 end
2539
2540 describe "pins" do
2541 setup do
2542 user = insert(:user)
2543
2544 [user: user, object_id: object_id_from_created_activity(user)]
2545 end
2546
2547 test "unique pins", %{user: user, object_id: object_id} do
2548 assert {:ok, %{pinned_objects: %{^object_id => pinned_at1} = pins} = updated_user} =
2549 User.add_pinned_object_id(user, object_id)
2550
2551 assert Enum.count(pins) == 1
2552
2553 assert {:ok, %{pinned_objects: %{^object_id => pinned_at2} = pins}} =
2554 User.add_pinned_object_id(updated_user, object_id)
2555
2556 assert pinned_at1 == pinned_at2
2557
2558 assert Enum.count(pins) == 1
2559 end
2560
2561 test "respects max_pinned_statuses limit", %{user: user, object_id: object_id} do
2562 clear_config([:instance, :max_pinned_statuses], 1)
2563 {:ok, updated} = User.add_pinned_object_id(user, object_id)
2564
2565 object_id2 = object_id_from_created_activity(user)
2566
2567 {:error, %{errors: errors}} = User.add_pinned_object_id(updated, object_id2)
2568 assert Keyword.has_key?(errors, :pinned_objects)
2569 end
2570
2571 test "remove_pinned_object_id/2", %{user: user, object_id: object_id} do
2572 assert {:ok, updated} = User.add_pinned_object_id(user, object_id)
2573
2574 {:ok, after_remove} = User.remove_pinned_object_id(updated, object_id)
2575 assert after_remove.pinned_objects == %{}
2576 end
2577 end
2578
2579 defp object_id_from_created_activity(user) do
2580 %{id: id} = insert(:note_activity, user: user)
2581 %{object: %{data: %{"id" => object_id}}} = Activity.get_by_id_with_object(id)
2582 object_id
2583 end
2584
2585 describe "add_alias/2" do
2586 test "should add alias for another user" do
2587 user = insert(:user)
2588 user2 = insert(:user)
2589
2590 assert {:ok, user_updated} = user |> User.add_alias(user2)
2591
2592 assert user_updated.also_known_as |> length() == 1
2593 assert user2.ap_id in user_updated.also_known_as
2594 end
2595
2596 test "should add multiple aliases" do
2597 user = insert(:user)
2598 user2 = insert(:user)
2599 user3 = insert(:user)
2600
2601 assert {:ok, user} = user |> User.add_alias(user2)
2602 assert {:ok, user_updated} = user |> User.add_alias(user3)
2603
2604 assert user_updated.also_known_as |> length() == 2
2605 assert user2.ap_id in user_updated.also_known_as
2606 assert user3.ap_id in user_updated.also_known_as
2607 end
2608
2609 test "should not add duplicate aliases" do
2610 user = insert(:user)
2611 user2 = insert(:user)
2612
2613 assert {:ok, user} = user |> User.add_alias(user2)
2614
2615 assert {:ok, user_updated} = user |> User.add_alias(user2)
2616
2617 assert user_updated.also_known_as |> length() == 1
2618 assert user2.ap_id in user_updated.also_known_as
2619 end
2620 end
2621
2622 describe "alias_users/1" do
2623 test "should get aliases for a user" do
2624 user = insert(:user)
2625 user2 = insert(:user, also_known_as: [user.ap_id])
2626
2627 aliases = user2 |> User.alias_users()
2628
2629 assert aliases |> length() == 1
2630
2631 alias_user = aliases |> Enum.at(0)
2632
2633 assert alias_user.ap_id == user.ap_id
2634 end
2635 end
2636
2637 describe "delete_alias/2" do
2638 test "should delete existing alias" do
2639 user = insert(:user)
2640 user2 = insert(:user, also_known_as: [user.ap_id])
2641
2642 assert {:ok, user_updated} = user2 |> User.delete_alias(user)
2643
2644 assert user_updated.also_known_as == []
2645 end
2646
2647 test "should report error on non-existing alias" do
2648 user = insert(:user)
2649 user2 = insert(:user)
2650 user3 = insert(:user, also_known_as: [user.ap_id])
2651
2652 assert {:error, :no_such_alias} = user3 |> User.delete_alias(user2)
2653
2654 user3_updated = User.get_cached_by_ap_id(user3.ap_id)
2655
2656 assert user3_updated.also_known_as |> length() == 1
2657 assert user.ap_id in user3_updated.also_known_as
2658 end
2659 end
2660 end