add accounts doctests

This commit is contained in:
shibao 2023-01-29 14:36:44 -05:00
parent 24f608163f
commit 2cb6aa8d33
2 changed files with 145 additions and 71 deletions

View File

@ -16,8 +16,9 @@ defmodule Memex.Accounts do
## Examples ## Examples
iex> get_user_by_email("foo@example.com") iex> register_user(%{email: "foo@example.com", password: "valid_password"})
%User{} iex> with %User{} <- get_user_by_email("foo@example.com"), do: :passed
:passed
iex> get_user_by_email("unknown@example.com") iex> get_user_by_email("unknown@example.com")
nil nil
@ -31,8 +32,9 @@ defmodule Memex.Accounts do
## Examples ## Examples
iex> get_user_by_email_and_password("foo@example.com", "correct_password") iex> register_user(%{email: "foo@example.com", password: "valid_password"})
%User{} iex> with %User{} <- get_user_by_email_and_password("foo@example.com", "valid_password"), do: :passed
:passed
iex> get_user_by_email_and_password("foo@example.com", "invalid_password") iex> get_user_by_email_and_password("foo@example.com", "invalid_password")
nil nil
@ -53,10 +55,11 @@ defmodule Memex.Accounts do
## Examples ## Examples
iex> get_user!(123) iex> {:ok, user} = register_user(%{email: "foo@example.com", password: "valid_password"})
%User{} iex> get_user!(user.id)
user
iex> get_user!(456) > get_user!()
** (Ecto.NoResultsError) ** (Ecto.NoResultsError)
""" """
@ -68,13 +71,15 @@ defmodule Memex.Accounts do
## Examples ## Examples
iex> list_users_by_role(%User{id: 123, role: :admin}) iex> {:ok, user1} = register_user(%{email: "foo1@example.com", password: "valid_password"})
[admin: [%User{}], user: [%User{}, %User{}]] iex> {:ok, user2} = register_user(%{email: "foo2@example.com", password: "valid_password"})
iex> with %{admin: [^user1], user: [^user2]} <- list_all_users_by_role(user1), do: :passed
:passed
""" """
@spec list_all_users_by_role(User.t()) :: %{String.t() => [User.t()]} @spec list_all_users_by_role(User.t()) :: %{User.role() => [User.t()]}
def list_all_users_by_role(%User{role: :admin}) do def list_all_users_by_role(%User{role: :admin}) do
Repo.all(from u in User, order_by: u.email) |> Enum.group_by(fn user -> user.role end) Repo.all(from u in User, order_by: u.email) |> Enum.group_by(fn %{role: role} -> role end)
end end
@doc """ @doc """
@ -82,13 +87,13 @@ defmodule Memex.Accounts do
## Examples ## Examples
iex> list_users_by_role(%User{id: 123, role: :admin}) iex> {:ok, user} = register_user(%{email: "foo@example.com", password: "valid_password"})
[%User{}] iex> with [^user] <- list_users_by_role(:admin), do: :passed
:passed
""" """
@spec list_users_by_role(User.role()) :: [User.t()] @spec list_users_by_role(:admin) :: [User.t()]
def list_users_by_role(role) do def list_users_by_role(:admin = role) do
role = role |> to_string()
Repo.all(from u in User, where: u.role == ^role, order_by: u.email) Repo.all(from u in User, where: u.role == ^role, order_by: u.email)
end end
@ -99,11 +104,13 @@ defmodule Memex.Accounts do
## Examples ## Examples
iex> register_user(%{field: value}) iex> with {:ok, %User{email: "foo@example.com"}} <-
{:ok, %User{}} ...> register_user(%{email: "foo@example.com", password: "valid_password"}),
...> do: :passed
:passed
iex> register_user(%{field: bad_value}) iex> with {:error, %Changeset{}} <- register_user(%{email: "foo@example"}), do: :passed
{:error, %Changeset{}} :passed
""" """
@spec register_user(attrs :: map()) :: {:ok, User.t()} | {:error, User.changeset()} @spec register_user(attrs :: map()) :: {:ok, User.t()} | {:error, User.changeset()}
@ -128,8 +135,11 @@ defmodule Memex.Accounts do
## Examples ## Examples
iex> change_user_registration(user) iex> with %Changeset{} <- change_user_registration(), do: :passed
%Changeset{data: %User{}} :passed
iex> with %Changeset{} <- change_user_registration(%{password: "hi"}), do: :passed
:passed
""" """
@spec change_user_registration() :: User.changeset() @spec change_user_registration() :: User.changeset()
@ -144,10 +154,11 @@ defmodule Memex.Accounts do
## Examples ## Examples
iex> change_user_email(user) iex> with %Changeset{} <- change_user_email(%User{email: "foo@example.com"}), do: :passed
%Changeset{data: %User{}} :passed
""" """
@spec change_user_email(User.t()) :: User.changeset()
@spec change_user_email(User.t(), attrs :: map()) :: User.changeset() @spec change_user_email(User.t(), attrs :: map()) :: User.changeset()
def change_user_email(user, attrs \\ %{}), do: User.email_changeset(user, attrs) def change_user_email(user, attrs \\ %{}), do: User.email_changeset(user, attrs)
@ -156,8 +167,8 @@ defmodule Memex.Accounts do
## Examples ## Examples
iex> change_user_role(user) iex> with %Changeset{} <- change_user_role(%User{}, :user), do: :passed
%Changeset{data: %User{}} :passed
""" """
@spec change_user_role(User.t(), User.role()) :: User.changeset() @spec change_user_role(User.t(), User.role()) :: User.changeset()
@ -169,14 +180,20 @@ defmodule Memex.Accounts do
## Examples ## Examples
iex> apply_user_email(user, "valid password", %{email: ...}) iex> {:ok, user} = register_user(%{email: "foo@example.com", password: "valid_password"})
{:ok, %User{}} iex> with {:ok, %User{}} <-
...> apply_user_email(user, "valid_password", %{email: "new_email@account.com"}),
...> do: :passed
:passed
iex> apply_user_email(user, "invalid password", %{email: ...}) iex> {:ok, user} = register_user(%{email: "foo@example.com", password: "valid_password"})
{:error, %Changeset{}} iex> with {:error, %Changeset{}} <-
...> apply_user_email(user, "invalid password", %{email: "new_email@account"}),
...> do: :passed
:passed
""" """
@spec apply_user_email(User.t(), password :: String.t(), attrs :: map()) :: @spec apply_user_email(User.t(), email :: String.t(), attrs :: map()) ::
{:ok, User.t()} | {:error, User.changeset()} {:ok, User.t()} | {:error, User.changeset()}
def apply_user_email(user, password, attrs) do def apply_user_email(user, password, attrs) do
user user
@ -200,7 +217,7 @@ defmodule Memex.Accounts do
{:ok, _} <- Repo.transaction(user_email_multi(user, email, context)) do {:ok, _} <- Repo.transaction(user_email_multi(user, email, context)) do
:ok :ok
else else
_ -> :error _error_tuple -> :error
end end
end end
@ -218,8 +235,12 @@ defmodule Memex.Accounts do
## Examples ## Examples
iex> deliver_update_email_instructions(user, current_email, &Routes.user_update_email_url(conn, :edit, &1)) iex> {:ok, %{id: user_id} = user} = register_user(%{email: "foo@example.com", password: "valid_password"})
{:ok, %{to: ..., body: ...}} iex> with %Oban.Job{
...> args: %{email: :update_email, user_id: ^user_id, attrs: %{url: "example url"}}
...> } <- deliver_update_email_instructions(user, "new_foo@example.com", fn _token -> "example url" end),
...> do: :passed
:passed
""" """
@spec deliver_update_email_instructions(User.t(), current_email :: String.t(), function) :: @spec deliver_update_email_instructions(User.t(), current_email :: String.t(), function) ::
@ -236,8 +257,8 @@ defmodule Memex.Accounts do
## Examples ## Examples
iex> change_user_password(user) iex> with %Changeset{} <- change_user_password(%User{}), do: :passed
%Changeset{data: %User{}} :passed
""" """
@spec change_user_password(User.t(), attrs :: map()) :: User.changeset() @spec change_user_password(User.t(), attrs :: map()) :: User.changeset()
@ -249,14 +270,23 @@ defmodule Memex.Accounts do
## Examples ## Examples
iex> update_user_password(user, "valid password", %{password: ...}) iex> {:ok, user} = register_user(%{email: "foo@example.com", password: "valid_password"})
{:ok, %User{}} iex> with {:ok, %User{}} <-
...> reset_user_password(user, %{
...> password: "new password",
...> password_confirmation: "new password"
...> }),
...> do: :passed
:passed
iex> update_user_password(user, "invalid password", %{password: ...}) iex> {:ok, user} = register_user(%{email: "foo@example.com", password: "valid_password"})
{:error, %Changeset{}} iex> with {:error, %Changeset{}} <-
...> update_user_password(user, "invalid password", %{password: "123"}),
...> do: :passed
:passed
""" """
@spec update_user_password(User.t(), password :: String.t(), attrs :: map()) :: @spec update_user_password(User.t(), String.t(), attrs :: map()) ::
{:ok, User.t()} | {:error, User.changeset()} {:ok, User.t()} | {:error, User.changeset()}
def update_user_password(user, password, attrs) do def update_user_password(user, password, attrs) do
changeset = changeset =
@ -270,17 +300,17 @@ defmodule Memex.Accounts do
|> Repo.transaction() |> Repo.transaction()
|> case do |> case do
{:ok, %{user: user}} -> {:ok, user} {:ok, %{user: user}} -> {:ok, user}
{:error, :user, changeset, _} -> {:error, changeset} {:error, :user, changeset, _changes_so_far} -> {:error, changeset}
end end
end end
@doc """ @doc """
Returns an `%Changeset{}` for changing the user locale. Returns an `Ecto.Changeset.t()` for changing the user locale.
## Examples ## Examples
iex> change_user_locale(user) iex> with %Changeset{} <- change_user_locale(%User{}), do: :passed
%Changeset{data: %User{}} :passed
""" """
@spec change_user_locale(User.t()) :: User.changeset() @spec change_user_locale(User.t()) :: User.changeset()
@ -291,11 +321,9 @@ defmodule Memex.Accounts do
## Examples ## Examples
iex> update_user_locale(user, "valid locale") iex> {:ok, user} = register_user(%{email: "foo@example.com", password: "valid_password"})
{:ok, %User{}} iex> with {:ok, %User{}} <- update_user_locale(user, "en_US"), do: :passed
:passed
iex> update_user_password(user, "invalid locale")
{:error, %Changeset{}}
""" """
@spec update_user_locale(User.t(), locale :: String.t()) :: @spec update_user_locale(User.t(), locale :: String.t()) ::
@ -308,11 +336,13 @@ defmodule Memex.Accounts do
## Examples ## Examples
iex> delete_user!(user_to_delete, %User{id: 123, role: :admin}) iex> {:ok, user} = register_user(%{email: "foo@example.com", password: "valid_password"})
%User{} iex> with %User{} <- delete_user!(user, %User{id: 123, role: :admin}), do: :passed
:passed
iex> delete_user!(%User{id: 123}, %User{id: 123}) iex> {:ok, user} = register_user(%{email: "foo@example.com", password: "valid_password"})
%User{} iex> with %User{} <- delete_user!(user, user), do: :passed
:passed
""" """
@spec delete_user!(user_to_delete :: User.t(), User.t()) :: User.t() @spec delete_user!(user_to_delete :: User.t(), User.t()) :: User.t()
@ -354,20 +384,40 @@ defmodule Memex.Accounts do
""" """
@spec allow_registration?() :: boolean() @spec allow_registration?() :: boolean()
def allow_registration? do def allow_registration? do
Application.get_env(:memex, MemexWeb.Endpoint)[:registration] == "public" or Application.get_env(:Memex, MemexWeb.Endpoint)[:registration] == "public" or
list_users_by_role(:admin) |> Enum.empty?() list_users_by_role(:admin) |> Enum.empty?()
end end
@doc """ @doc """
Checks if user is an admin Checks if user is an admin
## Examples
iex> {:ok, user} = register_user(%{email: "foo@example.com", password: "valid_password"})
iex> is_admin?(user)
true
iex> is_admin?(%User{id: Ecto.UUID.generate()})
false
""" """
@spec is_admin?(User.t()) :: boolean() @spec is_admin?(User.t()) :: boolean()
def is_admin?(%User{id: user_id}) do def is_admin?(%User{id: user_id}) do
Repo.exists?(from u in User, where: u.id == ^user_id and u.role == :admin) Repo.exists?(from u in User, where: u.id == ^user_id, where: u.role == :admin)
end end
@doc """ @doc """
Checks to see if user has the admin role Checks to see if user has the admin role
## Examples
iex> {:ok, user} = register_user(%{email: "foo@example.com", password: "valid_password"})
iex> is_already_admin?(user)
true
iex> is_already_admin?(%User{})
false
""" """
@spec is_already_admin?(User.t() | nil) :: boolean() @spec is_already_admin?(User.t() | nil) :: boolean()
def is_already_admin?(%User{role: :admin}), do: true def is_already_admin?(%User{role: :admin}), do: true
@ -380,10 +430,16 @@ defmodule Memex.Accounts do
## Examples ## Examples
iex> deliver_user_confirmation_instructions(user, &Routes.user_confirmation_url(conn, :confirm, &1)) iex> {:ok, %{id: user_id} = user} = register_user(%{email: "foo@example.com", password: "valid_password"})
{:ok, %{to: ..., body: ...}} iex> with %Oban.Job{
...> args: %{email: :welcome, user_id: ^user_id, attrs: %{url: "example url"}}
...> } <- deliver_user_confirmation_instructions(user, fn _token -> "example url" end),
...> do: :passed
:passed
iex> deliver_user_confirmation_instructions(confirmed_user, &Routes.user_confirmation_url(conn, :confirm, &1)) iex> {:ok, user} = register_user(%{email: "foo@example.com", password: "valid_password"})
iex> user = user |> User.confirm_changeset() |> Repo.update!()
iex> deliver_user_confirmation_instructions(user, fn _token -> "example url" end)
{:error, :already_confirmed} {:error, :already_confirmed}
""" """
@ -405,14 +461,14 @@ defmodule Memex.Accounts do
If the token matches, the user account is marked as confirmed If the token matches, the user account is marked as confirmed
and the token is deleted. and the token is deleted.
""" """
@spec confirm_user(token :: String.t()) :: {:ok, User.t()} | atom() @spec confirm_user(token :: String.t()) :: {:ok, User.t()} | :error
def confirm_user(token) do def confirm_user(token) do
with {:ok, query} <- UserToken.verify_email_token_query(token, "confirm"), with {:ok, query} <- UserToken.verify_email_token_query(token, "confirm"),
%User{} = user <- Repo.one(query), %User{} = user <- Repo.one(query),
{:ok, %{user: user}} <- Repo.transaction(confirm_user_multi(user)) do {:ok, %{user: user}} <- Repo.transaction(confirm_user_multi(user)) do
{:ok, user} {:ok, user}
else else
_ -> :error _error_tuple -> :error
end end
end end
@ -430,8 +486,12 @@ defmodule Memex.Accounts do
## Examples ## Examples
iex> deliver_user_reset_password_instructions(user, &Routes.user_reset_password_url(conn, :edit, &1)) iex> {:ok, %{id: user_id} = user} = register_user(%{email: "foo@example.com", password: "valid_password"})
{:ok, %{to: ..., body: ...}} iex> with %Oban.Job{args: %{
...> email: :reset_password, user_id: ^user_id, attrs: %{url: "example url"}}
...> } <- deliver_user_reset_password_instructions(user, fn _token -> "example url" end),
...> do: :passed
:passed
""" """
@spec deliver_user_reset_password_instructions(User.t(), function()) :: Job.t() @spec deliver_user_reset_password_instructions(User.t(), function()) :: Job.t()
@ -447,8 +507,11 @@ defmodule Memex.Accounts do
## Examples ## Examples
iex> get_user_by_reset_password_token("validtoken") iex> {:ok, user} = register_user(%{email: "foo@example.com", password: "valid_password"})
%User{} iex> {encoded_token, user_token} = UserToken.build_email_token(user, "reset_password")
iex> Repo.insert!(user_token)
iex> with %User{} <- get_user_by_reset_password_token(encoded_token), do: :passed
:passed
iex> get_user_by_reset_password_token("invalidtoken") iex> get_user_by_reset_password_token("invalidtoken")
nil nil
@ -460,7 +523,7 @@ defmodule Memex.Accounts do
%User{} = user <- Repo.one(query) do %User{} = user <- Repo.one(query) do
user user
else else
_ -> nil _error_tuple -> nil
end end
end end
@ -469,11 +532,20 @@ defmodule Memex.Accounts do
## Examples ## Examples
iex> reset_user_password(user, %{password: "new long password", password_confirmation: "new long password"}) iex> {:ok, user} = register_user(%{email: "foo@example.com", password: "valid_password"})
{:ok, %User{}} iex> with {:ok, %User{}} <-
...> reset_user_password(user, %{
...> password: "new password",
...> password_confirmation: "new password"
...> }),
...> do: :passed
:passed
iex> reset_user_password(user, %{password: "valid", password_confirmation: "not the same"}) iex> {:ok, user} = register_user(%{email: "foo@example.com", password: "valid_password"})
{:error, %Changeset{}} iex> with {:error, %Changeset{}} <-
...> reset_user_password(user, %{password: "valid", password_confirmation: "not the same"}),
...> do: :passed
:passed
""" """
@spec reset_user_password(User.t(), attrs :: map()) :: @spec reset_user_password(User.t(), attrs :: map()) ::
@ -485,7 +557,7 @@ defmodule Memex.Accounts do
|> Repo.transaction() |> Repo.transaction()
|> case do |> case do
{:ok, %{user: user}} -> {:ok, user} {:ok, %{user: user}} -> {:ok, user}
{:error, :user, changeset, _} -> {:error, changeset} {:error, :user, changeset, _changes_so_far} -> {:error, changeset}
end end
end end
end end

View File

@ -10,6 +10,8 @@ defmodule Memex.AccountsTest do
@moduletag :accounts_test @moduletag :accounts_test
doctest Accounts, import: true
describe "get_user_by_email/1" do describe "get_user_by_email/1" do
test "does not return the user if the email does not exist" do test "does not return the user if the email does not exist" do
refute Accounts.get_user_by_email("unknown@example.com") refute Accounts.get_user_by_email("unknown@example.com")