Remerge of hashtag following (#341)
[akkoma] / test / pleroma / user_test.exs
index 7e1e75404f9c7213ab90c1f8df811b34b5167d38..cc6634aba37197b8979a7a1ca7618ece7e13cd6b 100644 (file)
@@ -5,6 +5,7 @@
 defmodule Pleroma.UserTest do
   alias Pleroma.Activity
   alias Pleroma.Builders.UserBuilder
+  alias Pleroma.Notification
   alias Pleroma.Object
   alias Pleroma.Repo
   alias Pleroma.Tests.ObanHelpers
@@ -151,7 +152,7 @@ defmodule Pleroma.UserTest do
   test "ap_id returns the activity pub id for the user" do
     user = UserBuilder.build()
 
-    expected_ap_id = "#{Pleroma.Web.base_url()}/users/#{user.nickname}"
+    expected_ap_id = "#{Pleroma.Web.Endpoint.url()}/users/#{user.nickname}"
 
     assert expected_ap_id == User.ap_id(user)
   end
@@ -202,11 +203,11 @@ defmodule Pleroma.UserTest do
 
   test "doesn't return follow requests for deactivated accounts" do
     locked = insert(:user, is_locked: true)
-    pending_follower = insert(:user, %{deactivated: true})
+    pending_follower = insert(:user, %{is_active: false})
 
     CommonAPI.follow(pending_follower, locked)
 
-    assert true == pending_follower.deactivated
+    refute pending_follower.is_active
     assert [] = User.get_follow_requests(locked)
   end
 
@@ -275,7 +276,7 @@ defmodule Pleroma.UserTest do
 
   test "can't follow a deactivated users" do
     user = insert(:user)
-    followed = insert(:user, %{deactivated: true})
+    followed = insert(:user, %{is_active: false})
 
     {:error, _} = User.follow(user, followed)
   end
@@ -310,8 +311,8 @@ defmodule Pleroma.UserTest do
   describe "unfollow/2" do
     setup do: clear_config([:instance, :external_user_synchronization])
 
-    test "unfollow with syncronizes external user" do
-      Pleroma.Config.put([:instance, :external_user_synchronization], true)
+    test "unfollow with synchronizes external user" do
+      clear_config([:instance, :external_user_synchronization], true)
 
       followed =
         insert(:user,
@@ -396,7 +397,7 @@ defmodule Pleroma.UserTest do
       user = insert(:user)
       remote_user = insert(:user, %{local: false})
 
-      Pleroma.Config.put([:instance, :autofollowed_nicknames], [
+      clear_config([:instance, :autofollowed_nicknames], [
         user.nickname,
         remote_user.nickname
       ])
@@ -413,7 +414,7 @@ defmodule Pleroma.UserTest do
       user1 = insert(:user)
       user2 = insert(:user)
 
-      Pleroma.Config.put([:instance, :autofollowing_nicknames], [
+      clear_config([:instance, :autofollowing_nicknames], [
         user1.nickname,
         user2.nickname
       ])
@@ -428,9 +429,9 @@ defmodule Pleroma.UserTest do
 
     test "it sends a welcome message if it is set" do
       welcome_user = insert(:user)
-      Pleroma.Config.put([:welcome, :direct_message, :enabled], true)
-      Pleroma.Config.put([:welcome, :direct_message, :sender_nickname], welcome_user.nickname)
-      Pleroma.Config.put([:welcome, :direct_message, :message], "Hello, this is a direct message")
+      clear_config([:welcome, :direct_message, :enabled], true)
+      clear_config([:welcome, :direct_message, :sender_nickname], welcome_user.nickname)
+      clear_config([:welcome, :direct_message, :message], "Hello, this is a direct message")
 
       cng = User.register_changeset(%User{}, @full_user_data)
       {:ok, registered_user} = User.register(cng)
@@ -442,34 +443,21 @@ defmodule Pleroma.UserTest do
       assert activity.actor == welcome_user.ap_id
     end
 
-    test "it sends a welcome chat message if it is set" do
-      welcome_user = insert(:user)
-      Pleroma.Config.put([:welcome, :chat_message, :enabled], true)
-      Pleroma.Config.put([:welcome, :chat_message, :sender_nickname], welcome_user.nickname)
-      Pleroma.Config.put([:welcome, :chat_message, :message], "Hello, this is a chat message")
-
-      cng = User.register_changeset(%User{}, @full_user_data)
-      {:ok, registered_user} = User.register(cng)
-      ObanHelpers.perform_all()
-
-      activity = Repo.one(Pleroma.Activity)
-      assert registered_user.ap_id in activity.recipients
-      assert Object.normalize(activity, fetch: false).data["content"] =~ "chat message"
-      assert activity.actor == welcome_user.ap_id
-    end
-
     setup do:
-            clear_config(:mrf_simple,
-              media_removal: [],
-              media_nsfw: [],
-              federated_timeline_removal: [],
-              report_removal: [],
-              reject: [],
-              followers_only: [],
-              accept: [],
-              avatar_removal: [],
-              banner_removal: [],
-              reject_deletes: []
+            clear_config(
+              [:mrf_simple],
+              %{
+                media_removal: [],
+                media_nsfw: [],
+                federated_timeline_removal: [],
+                report_removal: [],
+                reject: [],
+                followers_only: [],
+                accept: [],
+                avatar_removal: [],
+                banner_removal: [],
+                reject_deletes: []
+              }
             )
 
     setup do:
@@ -479,30 +467,12 @@ defmodule Pleroma.UserTest do
               ]
             )
 
-    test "it sends a welcome chat message when Simple policy applied to local instance" do
-      Pleroma.Config.put([:mrf_simple, :media_nsfw], ["localhost"])
-
-      welcome_user = insert(:user)
-      Pleroma.Config.put([:welcome, :chat_message, :enabled], true)
-      Pleroma.Config.put([:welcome, :chat_message, :sender_nickname], welcome_user.nickname)
-      Pleroma.Config.put([:welcome, :chat_message, :message], "Hello, this is a chat message")
-
-      cng = User.register_changeset(%User{}, @full_user_data)
-      {:ok, registered_user} = User.register(cng)
-      ObanHelpers.perform_all()
-
-      activity = Repo.one(Pleroma.Activity)
-      assert registered_user.ap_id in activity.recipients
-      assert Object.normalize(activity, fetch: false).data["content"] =~ "chat message"
-      assert activity.actor == welcome_user.ap_id
-    end
-
     test "it sends a welcome email message if it is set" do
       welcome_user = insert(:user)
-      Pleroma.Config.put([:welcome, :email, :enabled], true)
-      Pleroma.Config.put([:welcome, :email, :sender], welcome_user.email)
+      clear_config([:welcome, :email, :enabled], true)
+      clear_config([:welcome, :email, :sender], welcome_user.email)
 
-      Pleroma.Config.put(
+      clear_config(
         [:welcome, :email, :subject],
         "Hello, welcome to cool site: <%= instance_name %>"
       )
@@ -522,7 +492,7 @@ defmodule Pleroma.UserTest do
     end
 
     test "it sends a confirm email" do
-      Pleroma.Config.put([:instance, :account_activation_required], true)
+      clear_config([:instance, :account_activation_required], true)
 
       cng = User.register_changeset(%User{}, @full_user_data)
       {:ok, registered_user} = User.register(cng)
@@ -551,8 +521,47 @@ defmodule Pleroma.UserTest do
       )
     end
 
+    test "it sends a registration confirmed email if no others will be sent" do
+      clear_config([:welcome, :email, :enabled], false)
+      clear_config([:instance, :account_activation_required], false)
+      clear_config([:instance, :account_approval_required], false)
+
+      {:ok, user} =
+        User.register_changeset(%User{}, @full_user_data)
+        |> User.register()
+
+      ObanHelpers.perform_all()
+
+      instance_name = Pleroma.Config.get([:instance, :name])
+      sender = Pleroma.Config.get([:instance, :notify_email])
+
+      assert_email_sent(
+        from: {instance_name, sender},
+        to: {user.name, user.email},
+        subject: "Account registered on #{instance_name}"
+      )
+    end
+
+    test "it fails gracefully with invalid email config" do
+      cng = User.register_changeset(%User{}, @full_user_data)
+
+      # Disable the mailer but enable all the things that want to send emails
+      clear_config([Pleroma.Emails.Mailer, :enabled], false)
+      clear_config([:instance, :account_activation_required], true)
+      clear_config([:instance, :account_approval_required], true)
+      clear_config([:welcome, :email, :enabled], true)
+      clear_config([:welcome, :email, :sender], "lain@lain.com")
+
+      # The user is still created
+      assert {:ok, %User{nickname: "nick"}} = User.register(cng)
+
+      # No emails are sent
+      ObanHelpers.perform_all()
+      refute_email_sent()
+    end
+
     test "it requires an email, name, nickname and password, bio is optional when account_activation_required is enabled" do
-      Pleroma.Config.put([:instance, :account_activation_required], true)
+      clear_config([:instance, :account_activation_required], true)
 
       @full_user_data
       |> Map.keys()
@@ -565,7 +574,7 @@ defmodule Pleroma.UserTest do
     end
 
     test "it requires an name, nickname and password, bio and email are optional when account_activation_required is disabled" do
-      Pleroma.Config.put([:instance, :account_activation_required], false)
+      clear_config([:instance, :account_activation_required], false)
 
       @full_user_data
       |> Map.keys()
@@ -614,26 +623,18 @@ defmodule Pleroma.UserTest do
       assert changeset.valid?
     end
 
-    test "it sets the password_hash and ap_id" do
+    test "it sets the password_hash, ap_id, private key and followers collection address" do
       changeset = User.register_changeset(%User{}, @full_user_data)
 
       assert changeset.valid?
 
       assert is_binary(changeset.changes[:password_hash])
+      assert is_binary(changeset.changes[:keys])
       assert changeset.changes[:ap_id] == User.ap_id(%User{nickname: @full_user_data.nickname})
-
+      assert is_binary(changeset.changes[:keys])
       assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers"
     end
 
-    test "it sets the 'accepts_chat_messages' set to true" do
-      changeset = User.register_changeset(%User{}, @full_user_data)
-      assert changeset.valid?
-
-      {:ok, user} = Repo.insert(changeset)
-
-      assert user.accepts_chat_messages
-    end
-
     test "it creates a confirmed user" do
       changeset = User.register_changeset(%User{}, @full_user_data)
       assert changeset.valid?
@@ -739,6 +740,123 @@ defmodule Pleroma.UserTest do
       freshed_user = refresh_record(user)
       assert freshed_user == fetched_user
     end
+
+    test "gets an existing user by nickname starting with http" do
+      user = insert(:user, nickname: "httpssome")
+      {:ok, fetched_user} = User.get_or_fetch("httpssome")
+
+      assert user == fetched_user
+    end
+  end
+
+  describe "get_or_fetch/1 remote users with tld, while BE is runned on subdomain" do
+    setup do: clear_config([Pleroma.Web.WebFinger, :update_nickname_on_user_fetch], true)
+
+    test "for mastodon" do
+      Tesla.Mock.mock(fn
+        %{url: "https://example.com/.well-known/host-meta"} ->
+          %Tesla.Env{
+            status: 302,
+            headers: [{"location", "https://sub.example.com/.well-known/host-meta"}]
+          }
+
+        %{url: "https://sub.example.com/.well-known/host-meta"} ->
+          %Tesla.Env{
+            status: 200,
+            body:
+              "test/fixtures/webfinger/masto-host-meta.xml"
+              |> File.read!()
+              |> String.replace("{{domain}}", "sub.example.com")
+          }
+
+        %{url: "https://sub.example.com/.well-known/webfinger?resource=acct:a@example.com"} ->
+          %Tesla.Env{
+            status: 200,
+            body:
+              "test/fixtures/webfinger/masto-webfinger.json"
+              |> File.read!()
+              |> String.replace("{{nickname}}", "a")
+              |> String.replace("{{domain}}", "example.com")
+              |> String.replace("{{subdomain}}", "sub.example.com"),
+            headers: [{"content-type", "application/jrd+json"}]
+          }
+
+        %{url: "https://sub.example.com/users/a"} ->
+          %Tesla.Env{
+            status: 200,
+            body:
+              "test/fixtures/webfinger/masto-user.json"
+              |> File.read!()
+              |> String.replace("{{nickname}}", "a")
+              |> String.replace("{{domain}}", "sub.example.com"),
+            headers: [{"content-type", "application/activity+json"}]
+          }
+
+        %{url: "https://sub.example.com/users/a/collections/featured"} ->
+          %Tesla.Env{
+            status: 200,
+            body:
+              File.read!("test/fixtures/users_mock/masto_featured.json")
+              |> String.replace("{{domain}}", "sub.example.com")
+              |> String.replace("{{nickname}}", "a"),
+            headers: [{"content-type", "application/activity+json"}]
+          }
+      end)
+
+      ap_id = "a@example.com"
+      {:ok, fetched_user} = User.get_or_fetch(ap_id)
+
+      assert fetched_user.ap_id == "https://sub.example.com/users/a"
+      assert fetched_user.nickname == "a@example.com"
+    end
+
+    test "for pleroma" do
+      Tesla.Mock.mock(fn
+        %{url: "https://example.com/.well-known/host-meta"} ->
+          %Tesla.Env{
+            status: 302,
+            headers: [{"location", "https://sub.example.com/.well-known/host-meta"}]
+          }
+
+        %{url: "https://sub.example.com/.well-known/host-meta"} ->
+          %Tesla.Env{
+            status: 200,
+            body:
+              "test/fixtures/webfinger/pleroma-host-meta.xml"
+              |> File.read!()
+              |> String.replace("{{domain}}", "sub.example.com")
+          }
+
+        %{url: "https://sub.example.com/.well-known/webfinger?resource=acct:a@example.com"} ->
+          %Tesla.Env{
+            status: 200,
+            body:
+              "test/fixtures/webfinger/pleroma-webfinger.json"
+              |> File.read!()
+              |> String.replace("{{nickname}}", "a")
+              |> String.replace("{{domain}}", "example.com")
+              |> String.replace("{{subdomain}}", "sub.example.com"),
+            headers: [{"content-type", "application/jrd+json"}]
+          }
+
+        %{url: "https://sub.example.com/users/a"} ->
+          %Tesla.Env{
+            status: 200,
+            body:
+              "test/fixtures/webfinger/pleroma-user.json"
+              |> File.read!()
+              |> String.replace("{{nickname}}", "a")
+              |> String.replace("{{domain}}", "sub.example.com"),
+            headers: [{"content-type", "application/activity+json"}]
+          }
+      end)
+
+      ap_id = "a@example.com"
+      {:ok, fetched_user} = User.get_or_fetch(ap_id)
+
+      assert fetched_user.ap_id == "https://sub.example.com/users/a"
+      assert fetched_user.nickname == "a@example.com"
+    end
   end
 
   describe "fetching a user from nickname or trying to build one" do
@@ -850,6 +968,25 @@ defmodule Pleroma.UserTest do
 
       assert user.last_refreshed_at == orig_user.last_refreshed_at
     end
+
+    test "it doesn't fail on invalid alsoKnownAs entries" do
+      Tesla.Mock.mock(fn
+        %{url: "https://mbp.example.com/"} ->
+          %Tesla.Env{
+            status: 200,
+            body:
+              "test/fixtures/microblogpub/user_with_invalid_also_known_as.json"
+              |> File.read!(),
+            headers: [{"content-type", "application/activity+json"}]
+          }
+
+        _ ->
+          %Tesla.Env{status: 404}
+      end)
+
+      assert {:ok, %User{also_known_as: []}} =
+               User.get_or_fetch_by_ap_id("https://mbp.example.com/")
+    end
   end
 
   test "returns an ap_id for a user" do
@@ -1209,7 +1346,7 @@ defmodule Pleroma.UserTest do
       collateral_user =
         insert(:user, %{ap_id: "https://another-awful-and-rude-instance.com/user/bully"})
 
-      {:ok, user} = User.block_domain(user, "*.awful-and-rude-instance.com")
+      {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
 
       refute User.blocks?(user, collateral_user)
     end
@@ -1227,7 +1364,7 @@ defmodule Pleroma.UserTest do
 
       user_domain = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
 
-      {:ok, user} = User.block_domain(user, "*.awful-and-rude-instance.com")
+      {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
 
       assert User.blocks?(user, user_from_subdomain)
       assert User.blocks?(user, user_with_two_subdomains)
@@ -1313,14 +1450,14 @@ defmodule Pleroma.UserTest do
     end
   end
 
-  describe ".deactivate" do
+  describe ".set_activation" do
     test "can de-activate then re-activate a user" do
       user = insert(:user)
-      assert false == user.deactivated
-      {:ok, user} = User.deactivate(user)
-      assert true == user.deactivated
-      {:ok, user} = User.deactivate(user, false)
-      assert false == user.deactivated
+      assert user.is_active
+      {:ok, user} = User.set_activation(user, false)
+      refute user.is_active
+      {:ok, user} = User.set_activation(user, true)
+      assert user.is_active
     end
 
     test "hide a user from followers" do
@@ -1328,7 +1465,7 @@ defmodule Pleroma.UserTest do
       user2 = insert(:user)
 
       {:ok, user, user2} = User.follow(user, user2)
-      {:ok, _user} = User.deactivate(user)
+      {:ok, _user} = User.set_activation(user, false)
 
       user2 = User.get_cached_by_id(user2.id)
 
@@ -1344,7 +1481,7 @@ defmodule Pleroma.UserTest do
       assert user2.following_count == 1
       assert User.following_count(user2) == 1
 
-      {:ok, _user} = User.deactivate(user)
+      {:ok, _user} = User.set_activation(user, false)
 
       user2 = User.get_cached_by_id(user2.id)
 
@@ -1374,7 +1511,7 @@ defmodule Pleroma.UserTest do
                  user: user2
                })
 
-      {:ok, _user} = User.deactivate(user)
+      {:ok, _user} = User.set_activation(user, false)
 
       assert [] == ActivityPub.fetch_public_activities(%{})
       assert [] == Pleroma.Notification.for_user(user2)
@@ -1544,7 +1681,7 @@ defmodule Pleroma.UserTest do
       follower = User.get_cached_by_id(follower.id)
 
       refute User.following?(follower, user)
-      assert %{deactivated: true} = User.get_by_id(user.id)
+      assert %{is_active: false} = User.get_by_id(user.id)
 
       assert [] == User.get_follow_requests(locked_user)
 
@@ -1600,13 +1737,13 @@ defmodule Pleroma.UserTest do
         follower_count: 9,
         following_count: 9001,
         is_locked: true,
-        is_confirmed: false,
+        is_confirmed: true,
         password_reset_pending: true,
-        is_approved: false,
+        is_approved: true,
         registration_reason: "ahhhhh",
         confirmation_token: "qqqq",
         domain_blocks: ["lain.com"],
-        deactivated: true,
+        is_active: false,
         ap_enabled: true,
         is_moderator: true,
         is_admin: true,
@@ -1630,8 +1767,8 @@ defmodule Pleroma.UserTest do
              email: nil,
              name: nil,
              password_hash: nil,
-             keys: nil,
-             public_key: nil,
+             keys: "RSA begin buplic key",
+             public_key: "--PRIVATE KEYE--",
              avatar: %{},
              tags: [],
              last_refreshed_at: nil,
@@ -1648,7 +1785,7 @@ defmodule Pleroma.UserTest do
              registration_reason: nil,
              confirmation_token: nil,
              domain_blocks: [],
-             deactivated: true,
+             is_active: false,
              ap_enabled: false,
              is_moderator: false,
              is_admin: false,
@@ -1663,6 +1800,56 @@ defmodule Pleroma.UserTest do
            } = user
   end
 
+  test "delete/1 purges a remote user" do
+    user =
+      insert(:user, %{
+        name: "qqqqqqq",
+        avatar: %{"a" => "b"},
+        banner: %{"a" => "b"},
+        local: false
+      })
+
+    {:ok, job} = User.delete(user)
+    {:ok, _} = ObanHelpers.perform(job)
+    user = User.get_by_id(user.id)
+
+    assert user.name == nil
+    assert user.avatar == %{}
+    assert user.banner == %{}
+  end
+
+  describe "set_suggestion" do
+    test "suggests a user" do
+      user = insert(:user, is_suggested: false)
+      refute user.is_suggested
+      {:ok, user} = User.set_suggestion(user, true)
+      assert user.is_suggested
+    end
+
+    test "suggests a list of users" do
+      unsuggested_users = [
+        insert(:user, is_suggested: false),
+        insert(:user, is_suggested: false),
+        insert(:user, is_suggested: false)
+      ]
+
+      {:ok, users} = User.set_suggestion(unsuggested_users, true)
+
+      assert Enum.count(users) == 3
+
+      Enum.each(users, fn user ->
+        assert user.is_suggested
+      end)
+    end
+
+    test "unsuggests a user" do
+      user = insert(:user, is_suggested: true)
+      assert user.is_suggested
+      {:ok, user} = User.set_suggestion(user, false)
+      refute user.is_suggested
+    end
+  end
+
   test "get_public_key_for_ap_id fetches a user that's not in the db" do
     assert {:ok, _key} = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin")
   end
@@ -1712,13 +1899,13 @@ defmodule Pleroma.UserTest do
     setup do: clear_config([:instance, :account_activation_required])
 
     test "return confirmation_pending for unconfirm user" do
-      Pleroma.Config.put([:instance, :account_activation_required], true)
+      clear_config([:instance, :account_activation_required], true)
       user = insert(:user, is_confirmed: false)
       assert User.account_status(user) == :confirmation_pending
     end
 
     test "return active for confirmed user" do
-      Pleroma.Config.put([:instance, :account_activation_required], true)
+      clear_config([:instance, :account_activation_required], true)
       user = insert(:user, is_confirmed: true)
       assert User.account_status(user) == :active
     end
@@ -1734,7 +1921,7 @@ defmodule Pleroma.UserTest do
     end
 
     test "returns :deactivated for deactivated user" do
-      user = insert(:user, local: true, is_confirmed: true, deactivated: true)
+      user = insert(:user, local: true, is_confirmed: true, is_active: false)
       assert User.account_status(user) == :deactivated
     end
 
@@ -1797,7 +1984,7 @@ defmodule Pleroma.UserTest do
     end
 
     test "returns false when the account is unconfirmed and confirmation is required" do
-      Pleroma.Config.put([:instance, :account_activation_required], true)
+      clear_config([:instance, :account_activation_required], true)
 
       user = insert(:user, local: true, is_confirmed: false)
       other_user = insert(:user, local: true)
@@ -1806,7 +1993,7 @@ defmodule Pleroma.UserTest do
     end
 
     test "returns true when the account is unconfirmed and confirmation is required but the account is remote" do
-      Pleroma.Config.put([:instance, :account_activation_required], true)
+      clear_config([:instance, :account_activation_required], true)
 
       user = insert(:user, local: false, is_confirmed: false)
       other_user = insert(:user, local: true)
@@ -1815,7 +2002,7 @@ defmodule Pleroma.UserTest do
     end
 
     test "returns true when the account is unconfirmed and being viewed by a privileged account (confirmation required)" do
-      Pleroma.Config.put([:instance, :account_activation_required], true)
+      clear_config([:instance, :account_activation_required], true)
 
       user = insert(:user, local: true, is_confirmed: false)
       other_user = insert(:user, local: true, is_admin: true)
@@ -1831,9 +2018,7 @@ defmodule Pleroma.UserTest do
       bio = "A.k.a. @nick@domain.com"
 
       expected_text =
-        ~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>)
+        ~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>)
 
       assert expected_text == User.parse_bio(bio, user)
     end
@@ -1885,7 +2070,7 @@ defmodule Pleroma.UserTest do
 
       users =
         Enum.map(1..total, fn _ ->
-          insert(:user, last_digest_emailed_at: days_ago(20), deactivated: false)
+          insert(:user, last_digest_emailed_at: days_ago(20), is_active: true)
         end)
 
       inactive_users_ids =
@@ -1903,7 +2088,7 @@ defmodule Pleroma.UserTest do
 
       users =
         Enum.map(1..total, fn _ ->
-          insert(:user, last_digest_emailed_at: days_ago(20), deactivated: false)
+          insert(:user, last_digest_emailed_at: days_ago(20), is_active: true)
         end)
 
       {inactive, active} = Enum.split(users, trunc(total / 2))
@@ -1936,7 +2121,7 @@ defmodule Pleroma.UserTest do
 
       users =
         Enum.map(1..total, fn _ ->
-          insert(:user, last_digest_emailed_at: days_ago(20), deactivated: false)
+          insert(:user, last_digest_emailed_at: days_ago(20), is_active: true)
         end)
 
       [sender | recipients] = users
@@ -1974,21 +2159,6 @@ defmodule Pleroma.UserTest do
     end
   end
 
-  describe "ensure_keys_present" do
-    test "it creates keys for a user and stores them in info" do
-      user = insert(:user)
-      refute is_binary(user.keys)
-      {:ok, user} = User.ensure_keys_present(user)
-      assert is_binary(user.keys)
-    end
-
-    test "it doesn't create keys if there already are some" do
-      user = insert(:user, keys: "xxx")
-      {:ok, user} = User.ensure_keys_present(user)
-      assert user.keys == "xxx"
-    end
-  end
-
   describe "get_ap_ids_by_nicknames" do
     test "it returns a list of AP ids for a given set of nicknames" do
       user = insert(:user)
@@ -2006,7 +2176,7 @@ defmodule Pleroma.UserTest do
       user1 = insert(:user, local: false, ap_id: "http://localhost:4001/users/masto_closed")
       user2 = insert(:user, local: false, ap_id: "http://localhost:4001/users/fuser2")
       insert(:user, local: true)
-      insert(:user, local: false, deactivated: true)
+      insert(:user, local: false, is_active: false)
       {:ok, user1: user1, user2: user2}
     end
 
@@ -2066,13 +2236,33 @@ defmodule Pleroma.UserTest do
       assert {:ok, user} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
       assert %User{bio: "test-bio"} = User.get_cached_by_ap_id(user.ap_id)
     end
+
+    test "removes report notifs when user isn't superuser any more" do
+      report_activity = insert(:report_activity)
+      user = insert(:user, is_moderator: true, is_admin: true)
+      {:ok, _} = Notification.create_notifications(report_activity)
+
+      assert [%Pleroma.Notification{type: "pleroma:report"}] = Notification.for_user(user)
+
+      {:ok, user} = user |> User.admin_api_update(%{is_moderator: false})
+      # is still superuser because still admin
+      assert [%Pleroma.Notification{type: "pleroma:report"}] = Notification.for_user(user)
+
+      {:ok, user} = user |> User.admin_api_update(%{is_moderator: true, is_admin: false})
+      # is still superuser because still moderator
+      assert [%Pleroma.Notification{type: "pleroma:report"}] = Notification.for_user(user)
+
+      {:ok, user} = user |> User.admin_api_update(%{is_moderator: false})
+      # is not a superuser any more
+      assert [] = Notification.for_user(user)
+    end
   end
 
   describe "following/followers synchronization" do
     setup do: clear_config([:instance, :external_user_synchronization])
 
     test "updates the counters normally on following/getting a follow when disabled" do
-      Pleroma.Config.put([:instance, :external_user_synchronization], false)
+      clear_config([:instance, :external_user_synchronization], false)
       user = insert(:user)
 
       other_user =
@@ -2092,8 +2282,8 @@ defmodule Pleroma.UserTest do
       assert other_user.follower_count == 1
     end
 
-    test "syncronizes the counters with the remote instance for the followed when enabled" do
-      Pleroma.Config.put([:instance, :external_user_synchronization], false)
+    test "synchronizes the counters with the remote instance for the followed when enabled" do
+      clear_config([:instance, :external_user_synchronization], false)
 
       user = insert(:user)
 
@@ -2108,14 +2298,14 @@ defmodule Pleroma.UserTest do
       assert other_user.following_count == 0
       assert other_user.follower_count == 0
 
-      Pleroma.Config.put([:instance, :external_user_synchronization], true)
+      clear_config([:instance, :external_user_synchronization], true)
       {:ok, _user, other_user} = User.follow(user, other_user)
 
       assert other_user.follower_count == 437
     end
 
-    test "syncronizes the counters with the remote instance for the follower when enabled" do
-      Pleroma.Config.put([:instance, :external_user_synchronization], false)
+    test "synchronizes the counters with the remote instance for the follower when enabled" do
+      clear_config([:instance, :external_user_synchronization], false)
 
       user = insert(:user)
 
@@ -2130,7 +2320,7 @@ defmodule Pleroma.UserTest do
       assert other_user.following_count == 0
       assert other_user.follower_count == 0
 
-      Pleroma.Config.put([:instance, :external_user_synchronization], true)
+      clear_config([:instance, :external_user_synchronization], true)
       {:ok, other_user, _user} = User.follow(other_user, user)
 
       assert other_user.following_count == 152
@@ -2142,11 +2332,40 @@ defmodule Pleroma.UserTest do
       [user: insert(:user)]
     end
 
-    test "blank email returns error", %{user: user} do
+    test "blank email returns error if we require an email on registration", %{user: user} do
+      orig_account_activation_required =
+        Pleroma.Config.get([:instance, :account_activation_required])
+
+      Pleroma.Config.put([:instance, :account_activation_required], true)
+
+      on_exit(fn ->
+        Pleroma.Config.put(
+          [:instance, :account_activation_required],
+          orig_account_activation_required
+        )
+      end)
+
       assert {:error, %{errors: [email: {"can't be blank", _}]}} = User.change_email(user, "")
       assert {:error, %{errors: [email: {"can't be blank", _}]}} = User.change_email(user, nil)
     end
 
+    test "blank email should be fine if we do not require an email on registration", %{user: user} do
+      orig_account_activation_required =
+        Pleroma.Config.get([:instance, :account_activation_required])
+
+      Pleroma.Config.put([:instance, :account_activation_required], false)
+
+      on_exit(fn ->
+        Pleroma.Config.put(
+          [:instance, :account_activation_required],
+          orig_account_activation_required
+        )
+      end)
+
+      assert {:ok, %User{email: nil}} = User.change_email(user, "")
+      assert {:ok, %User{email: nil}} = User.change_email(user, nil)
+    end
+
     test "non unique email returns error", %{user: user} do
       %{email: email} = insert(:user)
 
@@ -2162,6 +2381,25 @@ defmodule Pleroma.UserTest do
     test "changes email", %{user: user} do
       assert {:ok, %User{email: "cofe@cofe.party"}} = User.change_email(user, "cofe@cofe.party")
     end
+
+    test "adds email", %{user: user} do
+      orig_account_activation_required =
+        Pleroma.Config.get([:instance, :account_activation_required])
+
+      Pleroma.Config.put([:instance, :account_activation_required], false)
+
+      on_exit(fn ->
+        Pleroma.Config.put(
+          [:instance, :account_activation_required],
+          orig_account_activation_required
+        )
+      end)
+
+      assert {:ok, _} = User.change_email(user, "")
+      Pleroma.Config.put([:instance, :account_activation_required], true)
+
+      assert {:ok, %User{email: "cofe2@cofe.party"}} = User.change_email(user, "cofe2@cofe.party")
+    end
   end
 
   describe "get_cached_by_nickname_or_id" do
@@ -2177,43 +2415,43 @@ defmodule Pleroma.UserTest do
     test "allows getting remote users by id no matter what :limit_to_local_content is set to", %{
       remote_user: remote_user
     } do
-      Pleroma.Config.put([:instance, :limit_to_local_content], false)
+      clear_config([:instance, :limit_to_local_content], false)
       assert %User{} = User.get_cached_by_nickname_or_id(remote_user.id)
 
-      Pleroma.Config.put([:instance, :limit_to_local_content], true)
+      clear_config([:instance, :limit_to_local_content], true)
       assert %User{} = User.get_cached_by_nickname_or_id(remote_user.id)
 
-      Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
+      clear_config([:instance, :limit_to_local_content], :unauthenticated)
       assert %User{} = User.get_cached_by_nickname_or_id(remote_user.id)
     end
 
     test "disallows getting remote users by nickname without authentication when :limit_to_local_content is set to :unauthenticated",
          %{remote_user: remote_user} do
-      Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
+      clear_config([:instance, :limit_to_local_content], :unauthenticated)
       assert nil == User.get_cached_by_nickname_or_id(remote_user.nickname)
     end
 
     test "allows getting remote users by nickname with authentication when :limit_to_local_content is set to :unauthenticated",
          %{remote_user: remote_user, local_user: local_user} do
-      Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
+      clear_config([:instance, :limit_to_local_content], :unauthenticated)
       assert %User{} = User.get_cached_by_nickname_or_id(remote_user.nickname, for: local_user)
     end
 
     test "disallows getting remote users by nickname when :limit_to_local_content is set to true",
          %{remote_user: remote_user} do
-      Pleroma.Config.put([:instance, :limit_to_local_content], true)
+      clear_config([:instance, :limit_to_local_content], true)
       assert nil == User.get_cached_by_nickname_or_id(remote_user.nickname)
     end
 
     test "allows getting local users by nickname no matter what :limit_to_local_content is set to",
          %{local_user: local_user} do
-      Pleroma.Config.put([:instance, :limit_to_local_content], false)
+      clear_config([:instance, :limit_to_local_content], false)
       assert %User{} = User.get_cached_by_nickname_or_id(local_user.nickname)
 
-      Pleroma.Config.put([:instance, :limit_to_local_content], true)
+      clear_config([:instance, :limit_to_local_content], true)
       assert %User{} = User.get_cached_by_nickname_or_id(local_user.nickname)
 
-      Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
+      clear_config([:instance, :limit_to_local_content], :unauthenticated)
       assert %User{} = User.get_cached_by_nickname_or_id(local_user.nickname)
     end
   end
@@ -2232,6 +2470,36 @@ defmodule Pleroma.UserTest do
     end
   end
 
+  describe "local_nickname/1" do
+    test "returns nickname without host" do
+      assert User.local_nickname("@mentioned") == "mentioned"
+      assert User.local_nickname("a_local_nickname") == "a_local_nickname"
+      assert User.local_nickname("nickname@host.com") == "nickname"
+    end
+  end
+
+  describe "full_nickname/1" do
+    test "returns fully qualified nickname for local and remote users" do
+      local_user =
+        insert(:user, nickname: "local_user", ap_id: "https://somehost.com/users/local_user")
+
+      remote_user = insert(:user, nickname: "remote@host.com", local: false)
+
+      assert User.full_nickname(local_user) == "local_user@somehost.com"
+      assert User.full_nickname(remote_user) == "remote@host.com"
+    end
+
+    test "strips leading @ from mentions" do
+      assert User.full_nickname("@mentioned") == "mentioned"
+      assert User.full_nickname("@nickname@host.com") == "nickname@host.com"
+    end
+
+    test "does not modify nicknames" do
+      assert User.full_nickname("nickname") == "nickname"
+      assert User.full_nickname("nickname@host.com") == "nickname@host.com"
+    end
+  end
+
   test "avatar fallback" do
     user = insert(:user)
     assert User.avatar_url(user) =~ "/images/avi.png"
@@ -2248,4 +2516,237 @@ defmodule Pleroma.UserTest do
     user = insert(:user, ap_id: "https://lain.com/users/lain", nickname: "lain")
     assert User.get_host(user) == "lain.com"
   end
+
+  test "update_last_active_at/1" do
+    user = insert(:user)
+    assert is_nil(user.last_active_at)
+
+    test_started_at = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
+
+    assert {:ok, user} = User.update_last_active_at(user)
+
+    assert user.last_active_at >= test_started_at
+    assert user.last_active_at <= NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
+
+    last_active_at =
+      NaiveDateTime.utc_now()
+      |> NaiveDateTime.add(-:timer.hours(24), :millisecond)
+      |> NaiveDateTime.truncate(:second)
+
+    assert {:ok, user} =
+             user
+             |> cast(%{last_active_at: last_active_at}, [:last_active_at])
+             |> User.update_and_set_cache()
+
+    assert user.last_active_at == last_active_at
+    assert {:ok, user} = User.update_last_active_at(user)
+    assert user.last_active_at >= test_started_at
+    assert user.last_active_at <= NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
+  end
+
+  test "active_user_count/1" do
+    insert(:user)
+    insert(:user, %{local: false})
+    insert(:user, %{last_active_at: NaiveDateTime.utc_now()})
+    insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), days: -15)})
+    insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), weeks: -6)})
+    insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), months: -7)})
+    insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), years: -2)})
+
+    assert User.active_user_count() == 2
+    assert User.active_user_count(180) == 3
+    assert User.active_user_count(365) == 4
+    assert User.active_user_count(1000) == 5
+  end
+
+  describe "pins" do
+    setup do
+      user = insert(:user)
+
+      [user: user, object_id: object_id_from_created_activity(user)]
+    end
+
+    test "unique pins", %{user: user, object_id: object_id} do
+      assert {:ok, %{pinned_objects: %{^object_id => pinned_at1} = pins} = updated_user} =
+               User.add_pinned_object_id(user, object_id)
+
+      assert Enum.count(pins) == 1
+
+      assert {:ok, %{pinned_objects: %{^object_id => pinned_at2} = pins}} =
+               User.add_pinned_object_id(updated_user, object_id)
+
+      assert pinned_at1 == pinned_at2
+
+      assert Enum.count(pins) == 1
+    end
+
+    test "respects max_pinned_statuses limit", %{user: user, object_id: object_id} do
+      clear_config([:instance, :max_pinned_statuses], 1)
+      {:ok, updated} = User.add_pinned_object_id(user, object_id)
+
+      object_id2 = object_id_from_created_activity(user)
+
+      {:error, %{errors: errors}} = User.add_pinned_object_id(updated, object_id2)
+      assert Keyword.has_key?(errors, :pinned_objects)
+    end
+
+    test "remove_pinned_object_id/2", %{user: user, object_id: object_id} do
+      assert {:ok, updated} = User.add_pinned_object_id(user, object_id)
+
+      {:ok, after_remove} = User.remove_pinned_object_id(updated, object_id)
+      assert after_remove.pinned_objects == %{}
+    end
+  end
+
+  defp object_id_from_created_activity(user) do
+    %{id: id} = insert(:note_activity, user: user)
+    %{object: %{data: %{"id" => object_id}}} = Activity.get_by_id_with_object(id)
+    object_id
+  end
+
+  describe "add_alias/2" do
+    test "should add alias for another user" do
+      user = insert(:user)
+      user2 = insert(:user)
+
+      assert {:ok, user_updated} = user |> User.add_alias(user2)
+
+      assert user_updated.also_known_as |> length() == 1
+      assert user2.ap_id in user_updated.also_known_as
+    end
+
+    test "should add multiple aliases" do
+      user = insert(:user)
+      user2 = insert(:user)
+      user3 = insert(:user)
+
+      assert {:ok, user} = user |> User.add_alias(user2)
+      assert {:ok, user_updated} = user |> User.add_alias(user3)
+
+      assert user_updated.also_known_as |> length() == 2
+      assert user2.ap_id in user_updated.also_known_as
+      assert user3.ap_id in user_updated.also_known_as
+    end
+
+    test "should not add duplicate aliases" do
+      user = insert(:user)
+      user2 = insert(:user)
+
+      assert {:ok, user} = user |> User.add_alias(user2)
+
+      assert {:ok, user_updated} = user |> User.add_alias(user2)
+
+      assert user_updated.also_known_as |> length() == 1
+      assert user2.ap_id in user_updated.also_known_as
+    end
+  end
+
+  describe "alias_users/1" do
+    test "should get aliases for a user" do
+      user = insert(:user)
+      user2 = insert(:user, also_known_as: [user.ap_id])
+
+      aliases = user2 |> User.alias_users()
+
+      assert aliases |> length() == 1
+
+      alias_user = aliases |> Enum.at(0)
+
+      assert alias_user.ap_id == user.ap_id
+    end
+  end
+
+  describe "delete_alias/2" do
+    test "should delete existing alias" do
+      user = insert(:user)
+      user2 = insert(:user, also_known_as: [user.ap_id])
+
+      assert {:ok, user_updated} = user2 |> User.delete_alias(user)
+
+      assert user_updated.also_known_as == []
+    end
+
+    test "should report error on non-existing alias" do
+      user = insert(:user)
+      user2 = insert(:user)
+      user3 = insert(:user, also_known_as: [user.ap_id])
+
+      assert {:error, :no_such_alias} = user3 |> User.delete_alias(user2)
+
+      user3_updated = User.get_cached_by_ap_id(user3.ap_id)
+
+      assert user3_updated.also_known_as |> length() == 1
+      assert user.ap_id in user3_updated.also_known_as
+    end
+  end
+
+  describe "follow_hashtag/2" do
+    test "should follow a hashtag" do
+      user = insert(:user)
+      hashtag = insert(:hashtag)
+
+      assert {:ok, _} = user |> User.follow_hashtag(hashtag)
+
+      user = User.get_cached_by_ap_id(user.ap_id)
+
+      assert user.followed_hashtags |> Enum.count() == 1
+      assert hashtag.name in Enum.map(user.followed_hashtags, fn %{name: name} -> name end)
+    end
+
+    test "should not follow a hashtag twice" do
+      user = insert(:user)
+      hashtag = insert(:hashtag)
+
+      assert {:ok, _} = user |> User.follow_hashtag(hashtag)
+
+      assert {:ok, _} = user |> User.follow_hashtag(hashtag)
+
+      user = User.get_cached_by_ap_id(user.ap_id)
+
+      assert user.followed_hashtags |> Enum.count() == 1
+      assert hashtag.name in Enum.map(user.followed_hashtags, fn %{name: name} -> name end)
+    end
+
+    test "can follow multiple hashtags" do
+      user = insert(:user)
+      hashtag = insert(:hashtag)
+      other_hashtag = insert(:hashtag)
+
+      assert {:ok, _} = user |> User.follow_hashtag(hashtag)
+      assert {:ok, _} = user |> User.follow_hashtag(other_hashtag)
+
+      user = User.get_cached_by_ap_id(user.ap_id)
+
+      assert user.followed_hashtags |> Enum.count() == 2
+      assert hashtag.name in Enum.map(user.followed_hashtags, fn %{name: name} -> name end)
+      assert other_hashtag.name in Enum.map(user.followed_hashtags, fn %{name: name} -> name end)
+    end
+  end
+
+  describe "unfollow_hashtag/2" do
+    test "should unfollow a hashtag" do
+      user = insert(:user)
+      hashtag = insert(:hashtag)
+
+      assert {:ok, _} = user |> User.follow_hashtag(hashtag)
+      assert {:ok, _} = user |> User.unfollow_hashtag(hashtag)
+
+      user = User.get_cached_by_ap_id(user.ap_id)
+
+      assert user.followed_hashtags |> Enum.count() == 0
+    end
+
+    test "should not error when trying to unfollow a hashtag twice" do
+      user = insert(:user)
+      hashtag = insert(:hashtag)
+
+      assert {:ok, _} = user |> User.follow_hashtag(hashtag)
+      assert {:ok, _} = user |> User.unfollow_hashtag(hashtag)
+      assert {:ok, _} = user |> User.unfollow_hashtag(hashtag)
+
+      user = User.get_cached_by_ap_id(user.ap_id)
+
+      assert user.followed_hashtags |> Enum.count() == 0
+    end
+  end
 end