Merge branch 'develop' into feature/disable-account
authorEgor Kislitsyn <egor@kislitsyn.com>
Thu, 25 Apr 2019 06:41:10 +0000 (13:41 +0700)
committerEgor Kislitsyn <egor@kislitsyn.com>
Thu, 25 Apr 2019 06:41:10 +0000 (13:41 +0700)
1  2 
config/config.exs
docs/api/pleroma_api.md
lib/pleroma/notification.ex
lib/pleroma/user.ex
lib/pleroma/web/activity_pub/activity_pub.ex
lib/pleroma/web/router.ex
lib/pleroma/web/twitter_api/controllers/util_controller.ex
lib/pleroma/web/twitter_api/twitter_api.ex
test/user_test.exs
test/web/twitter_api/util_controller_test.exs

diff --combined config/config.exs
index 9dc9387c82593174f25403b19aefd259984b1c53,b11e4c680faf4d56d4a736c37eeeb36b921719f5..80f0c3f2590aefa2f5efc3553a48cb0006477d24
@@@ -230,7 -230,8 +230,8 @@@ config :pleroma, :instance
    welcome_user_nickname: nil,
    welcome_message: nil,
    max_report_comment_size: 1000,
-   safe_dm_mentions: false
+   safe_dm_mentions: false,
+   healthcheck: false
  
  config :pleroma, :markup,
    # XXX - unfortunately, inline images must be enabled by default right now, because
@@@ -413,8 -414,7 +414,8 @@@ config :pleroma_job_queue, :queues
    web_push: 50,
    mailer: 10,
    transmogrifier: 20,
 -  scheduled_activities: 10
 +  scheduled_activities: 10,
 +  user: 10
  
  config :pleroma, :fetch_initial_posts,
    enabled: false,
diff --combined docs/api/pleroma_api.md
index b9622f586c0ae7182eda4fbc4b4c55843750f75d,190846de937c963bf2920cb9f3700b3d911cb55c..dd0b6ca733dc79e47ccc75c4ef9aa7c4956d7ebc
@@@ -61,15 -61,6 +61,15 @@@ Request parameters can be passed via [q
  * Response: JSON. Returns `{"status": "success"}` if the deletion was successful, `{"error": "[error message]"}` otherwise
  * Example response: `{"error": "Invalid password."}`
  
 +## `/api/pleroma/disable_account`
 +### Disable an account
 +* Method `POST`
 +* Authentication: required
 +* Params:
 +    * `password`: user's password
 +* Response: JSON. Returns `{"status": "success"}` if the account was successfully disabled, `{"error": "[error message]"}` otherwise
 +* Example response: `{"error": "Invalid password."}`
 +
  ## `/api/account/register`
  ### Register a new user
  * Method `POST`
@@@ -86,7 -77,7 +86,7 @@@
      * `token`: invite token required when the registrations aren't public.
  * Response: JSON. Returns a user object on success, otherwise returns `{"error": "error_msg"}`
  * Example response:
- ```
+ ```json
  {
        "background_image": null,
        "cover_photo": "https://pleroma.soykaf.com/images/banner.png",
@@@ -196,6 -187,62 +196,62 @@@ See [Admin-API](Admin-API.md
  }
  ```
  
+ ## `/api/v1/pleroma/accounts/:id/favourites`
+ ### Returns favorites timeline of any user
+ * Method `GET`
+ * Authentication: not required
+ * Params:
+     * `id`: the id of the account for whom to return results
+     * `limit`: optional, the number of records to retrieve
+     * `since_id`: optional, returns results that are more recent than the specified id
+     * `max_id`: optional, returns results that are older than the specified id
+ * Response: JSON, returns a list of Mastodon Status entities on success, otherwise returns `{"error": "error_msg"}`
+ * Example response:
+ ```json
+ [
+   {
+     "account": {
+       "id": "9hptFmUF3ztxYh3Svg",
+       "url": "https://pleroma.example.org/users/nick2",
+       "username": "nick2",
+       ...
+     },
+     "application": {"name": "Web", "website": null},
+     "bookmarked": false,
+     "card": null,
+     "content": "This is :moominmamma: note 0",
+     "created_at": "2019-04-15T15:42:15.000Z",
+     "emojis": [],
+     "favourited": false,
+     "favourites_count": 1,
+     "id": "9hptFmVJ02khbzYJaS",
+     "in_reply_to_account_id": null,
+     "in_reply_to_id": null,
+     "language": null,
+     "media_attachments": [],
+     "mentions": [],
+     "muted": false,
+     "pinned": false,
+     "pleroma": {
+       "content": {"text/plain": "This is :moominmamma: note 0"},
+       "conversation_id": 13679,
+       "local": true,
+       "spoiler_text": {"text/plain": "2hu"}
+     },
+     "reblog": null,
+     "reblogged": false,
+     "reblogs_count": 0,
+     "replies_count": 0,
+     "sensitive": false,
+     "spoiler_text": "2hu",
+     "tags": [{"name": "2hu", "url": "/tag/2hu"}],
+     "uri": "https://pleroma.example.org/objects/198ed2a1-7912-4482-b559-244a0369e984",
+     "url": "https://pleroma.example.org/notice/9hptFmVJ02khbzYJaS",
+     "visibility": "public"
+   }
+ ]
+ ```
  ## `/api/pleroma/notification_settings`
  ### Updates user notification settings
  * Method `PUT`
      * `remote`: BOOLEAN field, receives notifications from people on remote instances
      * `local`: BOOLEAN field, receives notifications from people on the local instance
  * Response: JSON. Returns `{"status": "success"}` if the update was successful, otherwise returns `{"error": "error_msg"}`
+ ## `/api/pleroma/healthcheck`
+ ### Healthcheck endpoint with additional system data.
+ * Method `GET`
+ * Authentication: not required
+ * Params: none
+ * Response: JSON, statuses (200 - healthy, 503 unhealthy).
+ * Example response:
+ ```json
+ {
+   "pool_size": 0, # database connection pool
+   "active": 0, # active processes
+   "idle": 0, # idle processes
+   "memory_used": 0.00, # Memory used
+   "healthy": true # Instance state
+ }
+ ```
index 585157efece020946674f764f1adf9d49264b907,dd274cf6b22b8bdb2412b79d759f1bd7189e2239..8442643072cf5c9bc56bda9d0ce2d0116643b576
@@@ -33,13 -33,6 +33,13 @@@ defmodule Pleroma.Notification d
    def for_user_query(user) do
      Notification
      |> where(user_id: ^user.id)
 +    |> where(
 +      [n, a],
 +      fragment(
 +        "? not in (SELECT ap_id FROM users WHERE info->'deactivated' @> 'true')",
 +        a.actor
 +      )
 +    )
      |> join(:inner, [n], activity in assoc(n, :activity))
      |> join(:left, [n, a], object in Object,
        on:
  
    def skip?(:follows, activity, %{info: %{notification_settings: %{"follows" => false}}} = user) do
      actor = activity.data["actor"]
-     followed = User.get_by_ap_id(actor)
+     followed = User.get_cached_by_ap_id(actor)
      User.following?(user, followed)
    end
  
diff --combined lib/pleroma/user.ex
index 6aaa3244f41188da2181dec5ce5db4ca50d63284,f1feab279968d48eddd9c971fe9cc64a3d2dcaaa..d103cd809645b6db381916579c30813ab139224d
@@@ -107,8 -107,10 +107,8 @@@ defmodule Pleroma.User d
    def ap_followers(%User{} = user), do: "#{ap_id(user)}/followers"
  
    def user_info(%User{} = user) do
 -    oneself = if user.local, do: 1, else: 0
 -
      %{
 -      following_count: length(user.following) - oneself,
 +      following_count: following_count(user),
        note_count: user.info.note_count,
        follower_count: user.info.follower_count,
        locked: user.info.locked,
      }
    end
  
 +  defp restrict_deactivated(query) do
 +    from(u in query,
 +      where: not fragment("? \\? 'deactivated' AND ?->'deactivated' @> 'true'", u.info, u.info)
 +    )
 +  end
 +
 +  def following_count(%User{following: []}), do: 0
 +
 +  def following_count(%User{following: following, id: id}) do
 +    from(u in User,
 +      where: u.follower_address in ^following,
 +      where: u.id != ^id
 +    )
 +    |> restrict_deactivated()
 +    |> Repo.aggregate(:count, :id)
 +  end
 +
    def remote_user_creation(params) do
      params =
        params
    def register(%Ecto.Changeset{} = changeset) do
      with {:ok, user} <- Repo.insert(changeset),
           {:ok, user} <- autofollow_users(user),
+          {:ok, user} <- set_cache(user),
           {:ok, _} <- Pleroma.User.WelcomeMessage.post_welcome_message_to_user(user),
           {:ok, _} <- try_send_confirmation_email(user) do
        {:ok, user}
      name = List.last(String.split(ap_id, "/"))
      nickname = "#{name}@#{domain}"
  
-     get_by_nickname(nickname)
+     get_cached_by_nickname(nickname)
    end
  
-   def set_cache(user) do
+   def set_cache({:ok, user}), do: set_cache(user)
+   def set_cache({:error, err}), do: {:error, err}
+   def set_cache(%User{} = user) do
      Cachex.put(:user_cache, "ap_id:#{user.ap_id}", user)
      Cachex.put(:user_cache, "nickname:#{user.nickname}", user)
      Cachex.put(:user_cache, "user_info:#{user.id}", user_info(user))
          with [_nick, _domain] <- String.split(nickname, "@"),
               {:ok, user} <- fetch_by_nickname(nickname) do
            if Pleroma.Config.get([:fetch_initial_posts, :enabled]) do
+             # TODO turn into job
              {:ok, _} = Task.start(__MODULE__, :fetch_initial_posts, [user])
            end
  
        where: fragment("? <@ ?", ^[follower_address], u.following),
        where: u.id != ^id
      )
 +    |> restrict_deactivated()
    end
  
    def get_followers_query(user, page) do
        where: u.follower_address in ^following,
        where: u.id != ^id
      )
 +    |> restrict_deactivated()
    end
  
    def get_friends_query(user, page) do
  
      info_cng = User.Info.set_note_count(user.info, note_count)
  
 -    cng =
 -      change(user)
 -      |> put_embed(:info, info_cng)
 -
 -    update_and_set_cache(cng)
 +    user
 +    |> change()
 +    |> put_embed(:info, info_cng)
 +    |> update_and_set_cache()
    end
  
    def update_follower_count(%User{} = user) do
        |> where([u], ^user.follower_address in u.following)
        |> where([u], u.id != ^user.id)
        |> select([u], %{count: count(u.id)})
 +      |> restrict_deactivated()
  
      User
      |> where(id: ^user.id)
            ^processed_query
          )
      )
 +    |> restrict_deactivated()
    end
  
    defp trigram_search_subquery(term) do
        },
        where: fragment("trim(? || ' ' || coalesce(?, '')) % ?", u.nickname, u.name, ^term)
      )
 +    |> restrict_deactivated()
    end
  
    def blocks_import(%User{} = blocker, blocked_identifiers) when is_list(blocked_identifiers) do
  
    # helper to handle the block given only an actor's AP id
    def block(blocker, %{ap_id: ap_id}) do
-     block(blocker, User.get_by_ap_id(ap_id))
+     block(blocker, get_cached_by_ap_id(ap_id))
    end
  
    def unblock(blocker, %{ap_id: ap_id}) do
    end
  
    def subscribed_to?(user, %{ap_id: ap_id}) do
-     with %User{} = target <- User.get_by_ap_id(ap_id) do
+     with %User{} = target <- get_cached_by_ap_id(ap_id) do
        Enum.member?(target.info.subscribers, user.ap_id)
      end
    end
      )
    end
  
 +  def deactivate_async(user, status \\ true) do
 +    PleromaJobQueue.enqueue(:user, __MODULE__, [:deactivate_async, user, status])
 +  end
 +
 +  def perform(:deactivate_async, user, status), do: deactivate(user, status)
 +
    def deactivate(%User{} = user, status \\ true) do
      info_cng = User.Info.set_activation_status(user.info, status)
  
 -    cng =
 -      change(user)
 -      |> put_embed(:info, info_cng)
 +    with {:ok, friends} <- User.get_friends(user),
 +         {:ok, followers} <- User.get_followers(user),
 +         {:ok, user} <-
 +           user
 +           |> change()
 +           |> put_embed(:info, info_cng)
 +           |> update_and_set_cache() do
 +      Enum.each(followers, &invalidate_cache(&1))
 +      Enum.each(friends, &update_follower_count(&1))
  
 -    update_and_set_cache(cng)
 +      {:ok, user}
 +    end
    end
  
    def update_notification_settings(%User{} = user, settings \\ %{}) do
    end
  
    def get_or_fetch_by_ap_id(ap_id) do
-     user = get_by_ap_id(ap_id)
+     user = get_cached_by_ap_id(ap_id)
  
      if !is_nil(user) and !User.needs_update?(user) do
        user
    def get_or_create_instance_user do
      relay_uri = "#{Pleroma.Web.Endpoint.url()}/relay"
  
-     if user = get_by_ap_id(relay_uri) do
+     if user = get_cached_by_ap_id(relay_uri) do
        user
      else
        changes =
    defp blank?(n), do: n
  
    def insert_or_update_user(data) do
-     data =
-       data
-       |> Map.put(:name, blank?(data[:name]) || data[:nickname])
-     cs = User.remote_user_creation(data)
-     Repo.insert(cs, on_conflict: :replace_all, conflict_target: :nickname)
+     data
+     |> Map.put(:name, blank?(data[:name]) || data[:nickname])
+     |> remote_user_creation()
+     |> Repo.insert(on_conflict: :replace_all, conflict_target: :nickname)
+     |> set_cache()
    end
  
    def ap_enabled?(%User{local: true}), do: true
    # this is because we have synchronous follow APIs and need to simulate them
    # with an async handshake
    def wait_and_refresh(_, %User{local: true} = a, %User{local: true} = b) do
-     with %User{} = a <- User.get_by_id(a.id),
-          %User{} = b <- User.get_by_id(b.id) do
+     with %User{} = a <- User.get_cached_by_id(a.id),
+          %User{} = b <- User.get_cached_by_id(b.id) do
        {:ok, a, b}
      else
        _e ->
  
    def wait_and_refresh(timeout, %User{} = a, %User{} = b) do
      with :ok <- :timer.sleep(timeout),
-          %User{} = a <- User.get_by_id(a.id),
-          %User{} = b <- User.get_by_id(b.id) do
+          %User{} = a <- User.get_cached_by_id(a.id),
+          %User{} = b <- User.get_cached_by_id(b.id) do
        {:ok, a, b}
      else
        _e ->
    end
  
    def tag(nickname, tags) when is_binary(nickname),
-     do: tag(User.get_by_nickname(nickname), tags)
+     do: tag(get_by_nickname(nickname), tags)
  
    def tag(%User{} = user, tags),
      do: update_tags(user, Enum.uniq((user.tags || []) ++ normalize_tags(tags)))
    end
  
    def untag(nickname, tags) when is_binary(nickname),
-     do: untag(User.get_by_nickname(nickname), tags)
+     do: untag(get_by_nickname(nickname), tags)
  
    def untag(%User{} = user, tags),
      do: update_tags(user, (user.tags || []) -- normalize_tags(tags))
index a345372e29f113606995f829f739e5b7594ed48e,604ffae7b4e50df0d525e94c0f7dbda0711d217c..6bf54d1ccfe83fa7c4a8dd9c819d322b144f8273
@@@ -168,7 -168,7 +168,7 @@@ defmodule Pleroma.Web.ActivityPub.Activ
      public = "https://www.w3.org/ns/activitystreams#Public"
  
      if activity.data["type"] in ["Create", "Announce", "Delete"] do
-       object = Object.normalize(activity.data["object"])
+       object = Object.normalize(activity)
        Pleroma.Web.Streamer.stream("user", activity)
        Pleroma.Web.Streamer.stream("list", activity)
  
          if !Enum.member?(activity.data["cc"] || [], public) &&
               !Enum.member?(
                 activity.data["to"],
-                User.get_by_ap_id(activity.data["actor"]).follower_address
+                User.get_cached_by_ap_id(activity.data["actor"]).follower_address
               ),
             do: Pleroma.Web.Streamer.stream("direct", activity)
        end
      |> restrict_reblogs(opts)
      |> restrict_pinned(opts)
      |> restrict_muted_reblogs(opts)
 +    |> Activity.restrict_deactivated_users()
    end
  
    def fetch_activities(recipients, opts \\ %{}) do
    end
  
    def make_user_from_ap_id(ap_id) do
-     if _user = User.get_by_ap_id(ap_id) do
+     if _user = User.get_cached_by_ap_id(ap_id) do
        Transmogrifier.upgrade_user_from_ap_id(ap_id)
      else
        with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
index f475de639e2b6ad61cd9cb67f442a98d86431c25,ff4f08af57a58a16cf7db7d033577bece2349649..5f7617ece7bb011913c3cc9b741e4fd06b11d62b
@@@ -135,6 -135,7 +135,7 @@@ defmodule Pleroma.Web.Router d
      post("/password_reset", UtilController, :password_reset)
      get("/emoji", UtilController, :emoji)
      get("/captcha", UtilController, :captcha)
+     get("/healthcheck", UtilController, :healthcheck)
    end
  
    scope "/api/pleroma", Pleroma.Web do
        post("/change_password", UtilController, :change_password)
        post("/delete_account", UtilController, :delete_account)
        put("/notification_settings", UtilController, :update_notificaton_settings)
 +      post("/disable_account", UtilController, :disable_account)
      end
  
      scope [] do
        get("/accounts/:id", MastodonAPIController, :user)
  
        get("/search", MastodonAPIController, :search)
+       get("/pleroma/accounts/:id/favourites", MastodonAPIController, :user_favourites)
      end
    end
  
index 9b0cf2b07670b8b35a973aa11772b1d99831946b,1122e6c5d26eef2e385ac184861cbd8bd81e673e..6c8c2fe248a5d860bff3f4f15bff8b0c74ae30d9
@@@ -22,7 -22,7 +22,7 @@@ defmodule Pleroma.Web.TwitterAPI.UtilCo
  
    def show_password_reset(conn, %{"token" => token}) do
      with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
-          %User{} = user <- User.get_by_id(token.user_id) do
+          %User{} = user <- User.get_cached_by_id(token.user_id) do
        render(conn, "password_reset.html", %{
          token: token,
          user: user
    def do_remote_follow(conn, %{
          "authorization" => %{"name" => username, "password" => password, "id" => id}
        }) do
-     followee = User.get_by_id(id)
+     followee = User.get_cached_by_id(id)
      avatar = User.avatar_url(followee)
      name = followee.nickname
  
      with %User{} = user <- User.get_cached_by_nickname(username),
           true <- Pbkdf2.checkpw(password, user.password_hash),
-          %User{} = _followed <- User.get_by_id(id),
+          %User{} = _followed <- User.get_cached_by_id(id),
           {:ok, follower} <- User.follow(user, followee),
           {:ok, _activity} <- ActivityPub.follow(follower, followee) do
        conn
    end
  
    def do_remote_follow(%{assigns: %{user: user}} = conn, %{"user" => %{"id" => id}}) do
-     with %User{} = followee <- User.get_by_id(id),
+     with %User{} = followee <- User.get_cached_by_id(id),
           {:ok, follower} <- User.follow(user, followee),
           {:ok, _activity} <- ActivityPub.follow(follower, followee) do
        conn
      end
    end
  
 +  def disable_account(%{assigns: %{user: user}} = conn, params) do
 +    case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
 +      {:ok, user} ->
 +        User.deactivate_async(user)
 +        json(conn, %{status: "success"})
 +
 +      {:error, msg} ->
 +        json(conn, %{error: msg})
 +    end
 +  end
 +
    def captcha(conn, _params) do
      json(conn, Pleroma.Captcha.new())
    end
+   def healthcheck(conn, _params) do
+     info =
+       if Pleroma.Config.get([:instance, :healthcheck]) do
+         Pleroma.Healthcheck.system_info()
+       else
+         %{}
+       end
+     conn =
+       if info[:healthy] do
+         conn
+       else
+         Plug.Conn.put_status(conn, :service_unavailable)
+       end
+     json(conn, info)
+   end
  end
index c3f769c00377ee7c1535fa5d5ae7edec0048a10f,adeac6f3c71489aa8f746fd954b3e852de10cbb6..2353a95a8059de9d6639c4a4d49fe00caf4c620d
@@@ -231,19 -231,16 +231,19 @@@ defmodule Pleroma.Web.TwitterAPI.Twitte
    def get_user(user \\ nil, params) do
      case params do
        %{"user_id" => user_id} ->
 -        case target = User.get_cached_by_nickname_or_id(user_id) do
 +        case User.get_cached_by_nickname_or_id(user_id) do
            nil ->
              {:error, "No user with such user_id"}
  
 -          _ ->
 -            {:ok, target}
 +          %User{info: %{deactivated: true}} ->
 +            {:error, "User has been disabled"}
 +
 +          user ->
 +            {:ok, user}
          end
  
        %{"screen_name" => nickname} ->
-         case User.get_by_nickname(nickname) do
+         case User.get_cached_by_nickname(nickname) do
            nil -> {:error, "No user with such screen_name"}
            target -> {:ok, target}
          end
diff --combined test/user_test.exs
index a5f9318533d49ba4dd9ada1c17498543f281d7ee,42d570c50c214d8d2d93d66c68df6553f71e8c0e..2966d1f88b541269e98b189eaf7c9eb961efe698
@@@ -8,7 -8,6 +8,7 @@@ defmodule Pleroma.UserTest d
    alias Pleroma.Object
    alias Pleroma.Repo
    alias Pleroma.User
 +  alias Pleroma.Web.ActivityPub.ActivityPub
    alias Pleroma.Web.CommonAPI
  
    use Pleroma.DataCase
  
      {:ok, user} = User.follow(user, followed)
  
-     user = User.get_by_id(user.id)
+     user = User.get_cached_by_id(user.id)
  
-     followed = User.get_by_ap_id(followed.ap_id)
+     followed = User.get_cached_by_ap_id(followed.ap_id)
      assert followed.info.follower_count == 1
  
      assert User.ap_followers(followed) in user.following
  
      {:ok, user, _activity} = User.unfollow(user, followed)
  
-     user = User.get_by_id(user.id)
+     user = User.get_cached_by_id(user.id)
  
      assert user.following == []
    end
  
      {:error, _} = User.unfollow(user, user)
  
-     user = User.get_by_id(user.id)
+     user = User.get_cached_by_id(user.id)
      assert user.following == [user.ap_id]
    end
  
    test "fetches correct profile for nickname beginning with number" do
      # Use old-style integer ID to try to reproduce the problem
      user = insert(:user, %{id: 1080})
 -    userwithnumbers = insert(:user, %{nickname: "#{user.id}garbage"})
 -    assert userwithnumbers == User.get_cached_by_nickname_or_id(userwithnumbers.nickname)
 +    user_with_numbers = insert(:user, %{nickname: "#{user.id}garbage"})
 +    assert user_with_numbers == User.get_cached_by_nickname_or_id(user_with_numbers.nickname)
    end
  
    describe "user registration" do
  
        {:ok, res} = User.get_friends(user)
  
-       followed_one = User.get_by_ap_id(followed_one.ap_id)
-       followed_two = User.get_by_ap_id(followed_two.ap_id)
+       followed_one = User.get_cached_by_ap_id(followed_one.ap_id)
+       followed_two = User.get_cached_by_ap_id(followed_two.ap_id)
        assert Enum.member?(res, followed_one)
        assert Enum.member?(res, followed_two)
        refute Enum.member?(res, not_followed)
      test "it sets the info->note_count property" do
        note = insert(:note)
  
-       user = User.get_by_ap_id(note.data["actor"])
+       user = User.get_cached_by_ap_id(note.data["actor"])
  
        assert user.info.note_count == 0
  
  
      test "it increases the info->note_count property" do
        note = insert(:note)
-       user = User.get_by_ap_id(note.data["actor"])
+       user = User.get_cached_by_ap_id(note.data["actor"])
  
        assert user.info.note_count == 0
  
  
      test "it decreases the info->note_count property" do
        note = insert(:note)
-       user = User.get_by_ap_id(note.data["actor"])
+       user = User.get_cached_by_ap_id(note.data["actor"])
  
        assert user.info.note_count == 0
  
        assert User.following?(blocked, blocker)
  
        {:ok, blocker} = User.block(blocker, blocked)
-       blocked = User.get_by_id(blocked.id)
+       blocked = User.get_cached_by_id(blocked.id)
  
        assert User.blocks?(blocker, blocked)
  
        refute User.following?(blocked, blocker)
  
        {:ok, blocker} = User.block(blocker, blocked)
-       blocked = User.get_by_id(blocked.id)
+       blocked = User.get_cached_by_id(blocked.id)
  
        assert User.blocks?(blocker, blocked)
  
        assert User.following?(blocked, blocker)
  
        {:ok, blocker} = User.block(blocker, blocked)
-       blocked = User.get_by_id(blocked.id)
+       blocked = User.get_cached_by_id(blocked.id)
  
        assert User.blocks?(blocker, blocked)
  
      assert addressed in recipients
    end
  
 -  test ".deactivate can de-activate then re-activate a user" do
 -    user = insert(:user)
 -    assert false == user.info.deactivated
 -    {:ok, user} = User.deactivate(user)
 -    assert true == user.info.deactivated
 -    {:ok, user} = User.deactivate(user, false)
 -    assert false == user.info.deactivated
 +  describe ".deactivate" do
 +    test "can de-activate then re-activate a user" do
 +      user = insert(:user)
 +      assert false == user.info.deactivated
 +      {:ok, user} = User.deactivate(user)
 +      assert true == user.info.deactivated
 +      {:ok, user} = User.deactivate(user, false)
 +      assert false == user.info.deactivated
 +    end
 +
 +    test "hide a user from followers " do
 +      user = insert(:user)
 +      user2 = insert(:user)
 +
 +      {:ok, user} = User.follow(user, user2)
 +      {:ok, _user} = User.deactivate(user)
 +
 +      info = User.get_cached_user_info(user2)
 +
 +      assert info.follower_count == 0
 +      assert {:ok, []} = User.get_followers(user2)
 +    end
 +
 +    test "hide a user from friends" do
 +      user = insert(:user)
 +      user2 = insert(:user)
 +
 +      {:ok, user2} = User.follow(user2, user)
 +      assert User.following_count(user2) == 1
 +
 +      {:ok, _user} = User.deactivate(user)
 +
 +      info = User.get_cached_user_info(user2)
 +
 +      assert info.following_count == 0
 +      assert User.following_count(user2) == 0
 +      assert {:ok, []} = User.get_friends(user2)
 +    end
 +
 +    test "hide a user's statuses from timelines and notifications" do
 +      user = insert(:user)
 +      user2 = insert(:user)
 +
 +      {:ok, user2} = User.follow(user2, user)
 +
 +      {:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{user2.nickname}"})
 +
 +      [notification] = Pleroma.Notification.for_user(user2)
 +      assert notification.activity.id == activity.id
 +
 +      assert [activity] == ActivityPub.fetch_public_activities(%{})
 +
 +      assert [activity] ==
 +               ActivityPub.fetch_activities([user2.ap_id | user2.following], %{"user" => user2})
 +               |> ActivityPub.contain_timeline(user2)
 +
 +      {:ok, _user} = User.deactivate(user)
 +
 +      assert [] == ActivityPub.fetch_public_activities(%{})
 +      assert [] == Pleroma.Notification.for_user(user2)
 +
 +      assert [] ==
 +               ActivityPub.fetch_activities([user2.ap_id | user2.following], %{"user" => user2})
 +               |> ActivityPub.contain_timeline(user2)
 +    end
    end
  
    test ".delete_user_activities deletes all create activities" do
  
      {:ok, _} = User.delete(user)
  
-     followed = User.get_by_id(followed.id)
-     follower = User.get_by_id(follower.id)
-     user = User.get_by_id(user.id)
+     followed = User.get_cached_by_id(followed.id)
+     follower = User.get_cached_by_id(follower.id)
+     user = User.get_cached_by_id(user.id)
  
      assert user.info.deactivated
  
        results = User.search("http://mastodon.example.org/users/admin", resolve: true)
        result = results |> List.first()
  
-       user = User.get_by_ap_id("http://mastodon.example.org/users/admin")
+       user = User.get_cached_by_ap_id("http://mastodon.example.org/users/admin")
  
        assert length(results) == 1
        assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
index 0288e24d151b9fd3269dabae62f7f553fb193883,56474447b59cfa1cee948fffb39cb32829c48459..14a8225f097caac84a6c9de636e77357930fa36f
@@@ -246,21 -246,9 +246,27 @@@ defmodule Pleroma.Web.TwitterAPI.UtilCo
      end
    end
  
+   test "GET /api/pleroma/healthcheck", %{conn: conn} do
+     conn = get(conn, "/api/pleroma/healthcheck")
+     assert conn.status in [200, 503]
+   end
++
 +  describe "POST /api/pleroma/disable_account" do
 +    test "it returns HTTP 200", %{conn: conn} do
 +      user = insert(:user)
 +
 +      response =
 +        conn
 +        |> assign(:user, user)
 +        |> post("/api/pleroma/disable_account", %{"password" => "test"})
 +        |> json_response(:ok)
 +
 +      assert response == %{"status" => "success"}
 +
 +      user = User.get_cached_by_id(user.id)
 +
 +      assert user.info.deactivated == true
 +    end
 +  end
  end