diff --git a/config/test.exs b/config/test.exs index 5a8d144..8057604 100644 --- a/config/test.exs +++ b/config/test.exs @@ -9,7 +9,7 @@ config :pow_assent, PowAssent.Test.Phoenix.Endpoint, secret_key_base: String.duplicate("abcdefghijklmnopqrstuvxyz0123456789", 2), render_errors: [view: PowAssent.Test.Phoenix.ErrorView, accepts: ~w(html json)] -config :pow_assent, PowAssent.Test.Phoenix.MailerEndpoint, +config :pow_assent, PowAssent.Test.Phoenix.EndpointConfirmEmail, secret_key_base: String.duplicate("abcdefghijklmnopqrstuvxyz0123456789", 2), render_errors: [view: PowAssent.Test.Phoenix.ErrorView, accepts: ~w(html json)] diff --git a/lib/mix/tasks/pow_assent.install.ex b/lib/mix/tasks/pow_assent.install.ex index 79e6cac..39cbcff 100644 --- a/lib/mix/tasks/pow_assent.install.ex +++ b/lib/mix/tasks/pow_assent.install.ex @@ -8,24 +8,25 @@ defmodule Mix.Tasks.PowAssent.Install do """ use Mix.Task - alias Mix.Pow + alias Mix.Project alias Mix.Tasks.PowAssent.Ecto.Install - @switches [] - @default_opts [] - @doc false def run(args) do - Pow.no_umbrella!("pow_assent.install") + no_umbrella!() - args - |> Pow.parse_options(@switches, @default_opts) - |> run_ecto_install(args) + run_ecto_install(args) end - defp run_ecto_install(config, args) do + defp run_ecto_install(args) do Install.run(args) + end + + defp no_umbrella! do + if Project.umbrella?() do + Mix.raise("mix pow_assent.install can't be used in umbrella apps. Run mix pow_assent.ecto.install in your ecto app directory.") + end - config + :ok end end diff --git a/lib/pow_assent.ex b/lib/pow_assent.ex index 8953346..d56d3c4 100644 --- a/lib/pow_assent.ex +++ b/lib/pow_assent.ex @@ -42,6 +42,22 @@ defmodule PowAssent do error: :invalid_server_response } end + + @spec unreachable(atom(), binary(), term()) :: %__MODULE__{} + def unreachable(adapter, url, error) do + %__MODULE__{ + message: + """ + Server was unreachable with #{inspect adapter}. + + Failed with the following error: + #{inspect error} + + URL: #{url} + """, + error: :unreachable + } + end end defmodule ConfigurationError do diff --git a/lib/pow_assent/ecto/schema.ex b/lib/pow_assent/ecto/schema.ex index 2b92fda..3885768 100644 --- a/lib/pow_assent/ecto/schema.ex +++ b/lib/pow_assent/ecto/schema.ex @@ -84,22 +84,15 @@ defmodule PowAssent.Ecto.Schema do end defp maybe_set_confirmed_at(changeset) do - case Map.has_key?(changeset.data, :email) do - true -> set_confirmed_at(changeset) + case confirmable?(changeset) do + true -> PowEmailConfirmation.Ecto.Schema.confirm_email_changeset(changeset) false -> changeset end end - defp set_confirmed_at(changeset) do - confirmable? = Map.has_key?(changeset.data, :email_confirmed_at) - - case confirmable? do - true -> - now = Pow.Ecto.Schema.__timestamp_for__(changeset.data.__struct__, :email_confirmed_at) - Changeset.change(changeset, email_confirmed_at: now) - - false -> - changeset - end + defp confirmable?(changeset) do + Map.has_key?(changeset.data, :email) and + Map.has_key?(changeset.data, :email_confirmed_at) and + !Map.get(changeset.data, :unconfirmed_email) end end diff --git a/lib/pow_assent/ecto/user_identities/context.ex b/lib/pow_assent/ecto/user_identities/context.ex index 3c22bba..22ac4db 100644 --- a/lib/pow_assent/ecto/user_identities/context.ex +++ b/lib/pow_assent/ecto/user_identities/context.ex @@ -18,7 +18,8 @@ defmodule PowAssent.Ecto.UserIdentities.Context do end end - Remember to update configuration with `users_context: MyApp.Users`. + Remember to update configuration with + `user_identities_context: MyApp.UserIdentities`. The following Pow methods can be accessed: @@ -35,7 +36,8 @@ defmodule PowAssent.Ecto.UserIdentities.Context do """ alias Ecto.Changeset alias PowAssent.Config - require Ecto.Query + # import Ecto.Query, only: [where: 3, join: 3, select: 3] + import Ecto.Query @type user :: map() @type user_identity :: map() @@ -47,7 +49,7 @@ defmodule PowAssent.Ecto.UserIdentities.Context do | {:error, Changeset.t()} @callback create_user(binary(), binary(), map(), map()) :: {:ok, map()} - | {:error, {:bound_to_different_user | :missing_user_id_field, Changeset.t()}} + | {:error, {:bound_to_different_user | :invalid_user_id_field, Changeset.t()}} | {:error, Changeset.t()} @callback delete(user(), binary()) :: {:ok, {number(), nil}} | {:error, {:no_password, Changeset.t()}} @@ -99,16 +101,12 @@ defmodule PowAssent.Ecto.UserIdentities.Context do """ @spec get_user_by_provider_uid(binary(), binary(), Config.t()) :: user() | nil def get_user_by_provider_uid(provider, uid, config) do - repo = repo(config) - struct = user_identity_struct(config) - - struct - |> repo.get_by(provider: provider, uid: uid) - |> repo.preload(:user) - |> case do - nil -> nil - identity -> identity.user - end + config + |> user_identity_schema_mod() + |> where([i], i.provider == ^provider and i.uid == ^uid) + |> join(:left, [i], i in assoc(i, :user)) + |> select([_, u], u) + |> repo(config).one() end @doc """ @@ -118,12 +116,11 @@ defmodule PowAssent.Ecto.UserIdentities.Context do """ @spec create(user(), binary(), binary(), Config.t()) :: {:ok, user_identity()} | {:error, {:bound_to_different_user, map()}} | {:error, Changeset.t()} def create(user, provider, uid, config) do - repo = repo(config) user_identity = Ecto.build_assoc(user, :user_identities) user_identity |> user_identity.__struct__.changeset(%{provider: provider, uid: uid}) - |> repo.insert() + |> repo(config).insert() |> case do {:error, %{errors: [uid_provider: _]} = changeset} -> {:error, {:bound_to_different_user, changeset}} @@ -138,23 +135,22 @@ defmodule PowAssent.Ecto.UserIdentities.Context do User schema module and repo module will be fetched from config. """ - @spec create_user(binary(), binary(), map(), map(), Config.t()) :: {:ok, map()} | {:error, {:bound_to_different_user | :missing_user_id_field, Changeset.t()}} | {:error, Changeset.t()} + @spec create_user(binary(), binary(), map(), map(), Config.t()) :: {:ok, map()} | {:error, {:bound_to_different_user | :invalid_user_id_field, Changeset.t()}} | {:error, Changeset.t()} def create_user(provider, uid, params, user_id_params, config) do - repo = repo(config) - user_struct = user_struct(config) + user_mod = user_schema_mod(config) user_identity = %{provider: provider, uid: uid} - user = struct(user_struct) - user_id_field = user_struct.pow_user_id_field() + user_id_field = user_mod.pow_user_id_field() - user - |> user_struct.user_identity_changeset(user_identity, params, user_id_params) - |> repo.insert() + user_mod + |> struct() + |> user_mod.user_identity_changeset(user_identity, params, user_id_params) + |> repo(config).insert() |> case do {:error, %{changes: %{user_identities: [%{errors: [uid_provider: _]}]}} = changeset} -> {:error, {:bound_to_different_user, changeset}} {:error, %{errors: [{^user_id_field, _}]} = changeset} -> - {:error, {:missing_user_id_field, changeset}} + {:error, {:invalid_user_id_field, changeset}} {:error, changeset} -> {:error, changeset} @@ -177,19 +173,19 @@ defmodule PowAssent.Ecto.UserIdentities.Context do user.user_identities |> Enum.split_with(&(&1.provider == provider)) - |> maybe_delete(user, repo, config) + |> maybe_delete(user, repo) end - defp maybe_delete({user_identities, rest}, %{password_hash: password_hash}, repo, config) when length(rest) > 0 or not is_nil(password_hash) do - user_identity = user_identity_struct(config) - results = - user_identity - |> Ecto.Query.where([u], u.id in ^Enum.map(user_identities, &(&1.id))) + defp maybe_delete({user_identities, rest}, %{password_hash: password_hash} = user, repo) when length(rest) > 0 or not is_nil(password_hash) do + results = + user + |> Ecto.assoc(:user_identities) + |> where([i], i.id in ^Enum.map(user_identities, &(&1.id))) |> repo.delete_all() {:ok, results} end - defp maybe_delete(_any, user, _repo, _config) do + defp maybe_delete(_any, user, _repo) do changeset = user |> Changeset.change() @@ -205,24 +201,27 @@ defmodule PowAssent.Ecto.UserIdentities.Context do """ @spec all(user(), Config.t()) :: [map()] def all(user, config) do - repo = repo(config) - user |> Ecto.assoc(:user_identities) - |> repo.all() + |> repo(config).all() end - defp user_identity_struct(config) do - association = user_struct(config).__schema__(:association, :user_identities) || raise_no_user_identity_error() + defp user_identity_schema_mod(config) when is_list(config) do + config + |> user_schema_mod() + |> user_identity_schema_mod() + end + defp user_identity_schema_mod(user_mod) when is_atom(user_mod) do + association = user_mod.__schema__(:association, :user_identities) || raise_no_user_identity_error() association.queryable end @spec raise_no_user_identity_error :: no_return defp raise_no_user_identity_error do - Config.raise_error("The `:user` configuration option doesnt' have a `:user_identities` association.") + Config.raise_error("The `:user` configuration option doesn't have a `:user_identities` association.") end - def user_struct(config), do: Pow.Ecto.Context.user_schema_mod(config) - def repo(config), do: Pow.Ecto.Context.repo(config) + defdelegate user_schema_mod(config), to: Pow.Ecto.Context + defdelegate repo(config), to: Pow.Ecto.Context end diff --git a/lib/pow_assent/ecto/user_identities/schema.ex b/lib/pow_assent/ecto/user_identities/schema.ex index 7de1cec..7678bfe 100644 --- a/lib/pow_assent/ecto/user_identities/schema.ex +++ b/lib/pow_assent/ecto/user_identities/schema.ex @@ -40,21 +40,16 @@ defmodule PowAssent.Ecto.UserIdentities.Schema do quote do @behaviour unquote(__MODULE__) - import unquote(__MODULE__), only: [pow_assent_user_identity_fields: 0] - @pow_user_mod unquote(user_mod) @pow_assent_config unquote(config) @spec changeset(Ecto.Schema.t() | Changeset.t(), map()) :: Changeset.t() - def changeset(user_identity_or_changeset, attrs), - do: pow_assent_changeset(user_identity_or_changeset, attrs) - - @spec pow_assent_changeset(Ecto.Schema.t() | Changeset.t(), map()) :: Changeset.t() - def pow_assent_changeset(user_identity_or_changeset, attrs) do - unquote(__MODULE__).changeset(user_identity_or_changeset, attrs, unquote(config)) - end + def changeset(user_identity_or_changeset, attrs), do: pow_assent_changeset(user_identity_or_changeset, attrs) defoverridable unquote(__MODULE__) + + unquote(__MODULE__).__pow_assent_methods__() + unquote(__MODULE__).__register_fields__() end end @@ -66,12 +61,31 @@ defmodule PowAssent.Ecto.UserIdentities.Schema do quote do belongs_to :user, @pow_user_mod - for {name, type, opts} <- unquote(__MODULE__).Fields.attrs(@pow_assent_config) do - field(name, type, opts) + for {name, type, defaults} <- @pow_assent_fields do + field(name, type, defaults) + end + end + end + + @doc false + defmacro __pow_assent_methods__ do + quote do + import unquote(__MODULE__), only: [pow_assent_user_identity_fields: 0] + + @spec pow_assent_changeset(Ecto.Schema.t() | Changeset.t(), map()) :: Changeset.t() + def pow_assent_changeset(user_identity_or_changeset, attrs) do + unquote(__MODULE__).changeset(user_identity_or_changeset, attrs, @pow_assent_config) end end end + @doc false + defmacro __register_fields__ do + quote do + @pow_assent_fields unquote(__MODULE__).Fields.attrs(@pow_assent_config) + end + end + @doc """ Validates a user identity. """ diff --git a/lib/pow_assent/ecto/user_identities/schema/fields.ex b/lib/pow_assent/ecto/user_identities/schema/fields.ex index 7cf8dc5..11cc0a0 100644 --- a/lib/pow_assent/ecto/user_identities/schema/fields.ex +++ b/lib/pow_assent/ecto/user_identities/schema/fields.ex @@ -4,6 +4,11 @@ defmodule PowAssent.Ecto.UserIdentities.Schema.Fields do """ alias PowAssent.Config + @attrs [ + {:provider, :string, null: false}, + {:uid, :string, null: false} + ] + @doc """ List of attributes for the ecto schema. @@ -15,11 +20,7 @@ defmodule PowAssent.Ecto.UserIdentities.Schema.Fields do def attrs(config) do users_table = Config.get(config, :users_table, "users") - [ - {:user_id, {:references, users_table}}, - {:provider, :string, null: false}, - {:uid, :string, null: false} - ] + [{:user_id, {:references, users_table}}] ++ @attrs end @doc """ diff --git a/lib/pow_assent/ecto/user_identities/schema/module.ex b/lib/pow_assent/ecto/user_identities/schema/module.ex index 1fa5bdd..b10f578 100644 --- a/lib/pow_assent/ecto/user_identities/schema/module.ex +++ b/lib/pow_assent/ecto/user_identities/schema/module.ex @@ -11,9 +11,7 @@ defmodule PowAssent.Ecto.UserIdentities.Schema.Module do @template """ defmodule <%= inspect schema.module %> do use Ecto.Schema - use PowAssent.Ecto.UserIdentities.Schema, - user: <%= inspect schema.user_module %> - + use PowAssent.Ecto.UserIdentities.Schema, user: <%= inspect(schema.user_module) %> <%= if schema.binary_id do %> @primary_key {:id, :binary_id, autogenerate: true} @foreign_key_type :binary_id<% end %> diff --git a/lib/pow_assent/http_adapter/httpc.ex b/lib/pow_assent/http_adapter/httpc.ex index d37ee38..fd5054b 100644 --- a/lib/pow_assent/http_adapter/httpc.ex +++ b/lib/pow_assent/http_adapter/httpc.ex @@ -51,8 +51,7 @@ defmodule PowAssent.HTTPAdapter.Httpc do {:ok, %HTTPResponse{status: status, headers: headers, body: body}} end - defp format_response({:error, {:failed_connect, _}}), do: {:error, :econnrefused} - defp format_response(response), do: response + defp format_response({:error, error}), do: {:error, error} defp parse_httpc_opts(nil, url), do: default_httpc_opts(url) defp parse_httpc_opts(opts, _url), do: opts diff --git a/lib/pow_assent/http_adapter/mint.ex b/lib/pow_assent/http_adapter/mint.ex index 69c0e11..a5ce908 100644 --- a/lib/pow_assent/http_adapter/mint.ex +++ b/lib/pow_assent/http_adapter/mint.ex @@ -58,8 +58,7 @@ defmodule PowAssent.HTTPAdapter.Mint do {:ok, %HTTPResponse{status: status, headers: headers, body: body}} end - defp format_response({:error, {:tls_alert, _tls_error}}), do: {:error, :econnrefused} - defp format_response(response), do: response + defp format_response({:error, response}), do: {:error, response} defp merge_body([{:data, _request, new_body} | rest], body), do: merge_body(rest, body <> new_body) defp merge_body(_rest, body), do: body diff --git a/lib/pow_assent/operations.ex b/lib/pow_assent/operations.ex index 574cb14..c9f15db 100644 --- a/lib/pow_assent/operations.ex +++ b/lib/pow_assent/operations.ex @@ -42,7 +42,7 @@ defmodule PowAssent.Operations do This calls `Pow.Ecto.UserIdentities.Context.create_user/5` or `create_user/4` on a custom context module. """ - @spec create_user(binary(), binary(), map(), map(), Config.t()) :: {:ok, map()} | {:error, {:bound_to_different_user | :missing_user_id_field, map()}} | {:error, map()} | no_return + @spec create_user(binary(), binary(), map(), map(), Config.t()) :: {:ok, map()} | {:error, {:bound_to_different_user | :invalid_user_id_field, map()}} | {:error, map()} | no_return def create_user(provider, uid, params, user_id_params, config) do case context_module(config) do Context -> Context.create_user(provider, uid, params, user_id_params, config) diff --git a/lib/pow_assent/phoenix/controllers/authorization_controller.ex b/lib/pow_assent/phoenix/controllers/authorization_controller.ex index efe4a16..e6f5c58 100644 --- a/lib/pow_assent/phoenix/controllers/authorization_controller.ex +++ b/lib/pow_assent/phoenix/controllers/authorization_controller.ex @@ -64,7 +64,7 @@ defmodule PowAssent.Phoenix.AuthorizationController do |> put_flash(:error, messages(conn).account_already_bound_to_other_user(conn)) |> redirect(to: routes(conn).registration_path(conn, :new)) end - def respond_callback({:error, {:missing_user_id_field, _changeset}, conn}) do + def respond_callback({:error, {:invalid_user_id_field, _changeset}, conn}) do conn |> put_session("pow_assent_params", conn.private[:pow_assent_params]) |> redirect(to: routes(conn).path_for(conn, RegistrationController, :add_user_id, [conn.params["provider"]])) @@ -113,6 +113,5 @@ defmodule PowAssent.Phoenix.AuthorizationController do assign(conn, :callback_url, url) end - defp handle_strategy_error(:econnrefused), do: raise "Connection refused" defp handle_strategy_error(error), do: raise error end diff --git a/lib/pow_assent/phoenix/controllers/registration_controller.ex b/lib/pow_assent/phoenix/controllers/registration_controller.ex index d0b9460..75c647f 100644 --- a/lib/pow_assent/phoenix/controllers/registration_controller.ex +++ b/lib/pow_assent/phoenix/controllers/registration_controller.ex @@ -41,7 +41,7 @@ defmodule PowAssent.Phoenix.RegistrationController do |> put_flash(:error, messages(conn).invalid_request(conn)) |> redirect(to: routes(conn).after_sign_out_path(conn)) end - def respond_create({:error, {:missing_user_id_field, changeset}, conn}), + def respond_create({:error, {:invalid_user_id_field, changeset}, conn}), do: respond_create({:error, changeset, conn}) def respond_create({:error, changeset, conn}) do conn diff --git a/lib/pow_assent/plug.ex b/lib/pow_assent/plug.ex index 3ce2de8..d8329a0 100644 --- a/lib/pow_assent/plug.ex +++ b/lib/pow_assent/plug.ex @@ -1,6 +1,18 @@ defmodule PowAssent.Plug do @moduledoc """ Plug helper methods. + + If you wish to configure PowAssent through the Pow plug interface rather than + environment config, please add PowAssent config with `:pow_assent` config: + + plug Pow.Plug.Session, + repo: MyApp.Repo, + user: MyApp.User, + pow_assent: [ + http_adapter: PowAssent.HTTPAdapter.Mint, + json_library: Poison, + user_identities_context: MyApp.UserIdentities + ] """ alias Plug.Conn alias PowAssent.{Config, Operations} @@ -32,18 +44,18 @@ defmodule PowAssent.Plug do authenticated. """ @spec callback(Conn.t(), binary(), map()) :: {:ok, map(), Conn.t()} | - {:error, {:bound_to_different_user | :missing_user_id_field, map()}, Conn.t()} | + {:error, {:bound_to_different_user | :invalid_user_id_field, map()}, Conn.t()} | {:error, {:strategy, any()}, Conn.t()} | {:error, map(), Conn.t()} def callback(conn, provider, params) do - pow_config = Pow.Plug.fetch_config(conn) + config = fetch_config(conn) user = Pow.Plug.current_user(conn) - {strategy, provider_config} = get_provider_config(pow_config, provider) + {strategy, provider_config} = get_provider_config(config, provider) provider_config |> strategy.callback(conn, params) |> parse_callback_response() - |> get_or_create_by_identity(provider, user, pow_config) + |> get_or_create_by_identity(provider, user, config) end defp parse_callback_response({:ok, %{user: params, conn: conn}}) do @@ -86,7 +98,7 @@ defmodule PowAssent.Plug do """ @spec create_user(Conn.t(), binary(), map(), map()) :: {:ok, map(), Conn.t()} | {:error, map(), Conn.t()} def create_user(conn, provider, params, user_id_params) do - config = Pow.Plug.fetch_config(conn) + config = fetch_config(conn) uid = params["uid"] provider @@ -102,7 +114,7 @@ defmodule PowAssent.Plug do """ @spec delete_identity(Conn.t(), binary()) :: {:ok, map(), Conn.t()} | {:error, any(), Conn.t()} def delete_identity(conn, provider) do - config = Pow.Plug.fetch_config(conn) + config = fetch_config(conn) conn |> Pow.Plug.current_user() @@ -118,7 +130,7 @@ defmodule PowAssent.Plug do """ @spec providers_for_current_user(Conn.t()) :: [atom()] def providers_for_current_user(conn) do - config = Pow.Plug.fetch_config(conn) + config = fetch_config(conn) conn |> Pow.Plug.current_user() @@ -135,7 +147,7 @@ defmodule PowAssent.Plug do @spec available_providers(Conn.t() | Config.t()) :: [atom()] def available_providers(%Conn{} = conn) do conn - |> fetch_pow_assent_config() + |> fetch_config() |> available_providers() end def available_providers(config) do @@ -144,9 +156,17 @@ defmodule PowAssent.Plug do |> Keyword.keys() end + defp fetch_config(conn) do + config = Pow.Plug.fetch_config(conn) + + config + |> Keyword.take([:otp_app, :mod, :repo, :user]) + |> Keyword.merge(Keyword.get(config, :pow_assent, [])) + end + defp get_provider_config(%Conn{} = conn, provider) do conn - |> fetch_pow_assent_config() + |> fetch_config() |> get_provider_config(provider) end defp get_provider_config(config, provider) do @@ -158,25 +178,5 @@ defmodule PowAssent.Plug do {strategy, provider_config} end - defp fetch_pow_assent_config(%Conn{} = conn) do - conn - |> Pow.Plug.fetch_config() - |> fetch_pow_assent_config() - end - defp fetch_pow_assent_config(config) do - config - |> Keyword.get(:pow_assent, []) - |> maybe_merge_otp_app(config) - end - - defp maybe_merge_otp_app(config, pow_config) do - pow_config - |> Pow.Config.get(:otp_app) - |> case do - nil -> config - otp_app -> Config.put(config, :otp_app, otp_app) - end - end - defp get_mod(config), do: config[:mod] end diff --git a/lib/pow_assent/strategies/azure_oauth2.ex b/lib/pow_assent/strategies/azure_oauth2.ex index 38f1e56..6654600 100644 --- a/lib/pow_assent/strategies/azure_oauth2.ex +++ b/lib/pow_assent/strategies/azure_oauth2.ex @@ -77,8 +77,8 @@ defmodule PowAssent.Strategy.AzureOAuth2 do def normalize(_config, user) do {:ok, %{ "uid" => user["sub"], - "name" => user["name"], - "email" => user["email"], + "name" => "#{user["given_name"]} #{user["family_name"]}", + "email" => user["email"] || user["upn"], "first_name" => user["given_name"], "last_name" => user["family_name"]}} end diff --git a/lib/pow_assent/strategy.ex b/lib/pow_assent/strategy.ex index a2acba1..91ff91b 100644 --- a/lib/pow_assent/strategy.ex +++ b/lib/pow_assent/strategy.ex @@ -19,7 +19,7 @@ defmodule PowAssent.Strategy do end """ alias Plug.Conn - alias PowAssent.{Config, HTTPResponse} + alias PowAssent.{Config, HTTPResponse, RequestError} @callback authorize_url(Keyword.t(), Conn.t()) :: {:ok, %{:conn => Conn.t(), :url => binary(), optional(atom()) => any()}} @@ -44,7 +44,7 @@ defmodule PowAssent.Strategy do method |> http_adapter.request(url, body, headers, opts) - |> parse_status_response() + |> parse_status_response(http_adapter, url) end defp fetch_http_adapter(config) do @@ -54,14 +54,16 @@ defmodule PowAssent.Strategy do end end - defp parse_status_response({:ok, %{status: status} = resp}) when status in 200..399 do + defp parse_status_response({:ok, %{status: status} = resp}, _http_adapter, _url) when status in 200..399 do {:ok, resp} end - defp parse_status_response({:ok, %{status: status} = resp}) when status in 400..599 do + defp parse_status_response({:ok, %{status: status} = resp}, _http_adapter, _url) when status in 400..599 do {:error, resp} end - defp parse_status_response({:error, reason}) do - {:error, reason} + defp parse_status_response({:error, error}, http_adapter, url) do + [url | _rest] = String.split(url, "?", parts: 2) + + {:error, RequestError.unreachable(http_adapter, url, error)} end @doc """ @@ -100,8 +102,7 @@ defmodule PowAssent.Strategy do @doc """ Decode a JSON response to a map """ - @spec decode_json!(binary() | map(), Keyword.t()) :: map() - def decode_json!(map, _config) when is_map(map), do: map + @spec decode_json!(binary(), Keyword.t()) :: map() def decode_json!(response, config) do json_library = Config.get(config, :json_library, default_json_library()) json_library.decode!(response) diff --git a/mix.exs b/mix.exs index bae1df8..812cb1b 100644 --- a/mix.exs +++ b/mix.exs @@ -19,11 +19,7 @@ defmodule PowAssent.MixProject do # Docs name: "PowAssent", - docs: docs(), - - dialyzer: [ - ignore_warnings: ".dialyzer_ignore", - ] + docs: docs() ] end diff --git a/test/mix/tasks/phoenix/pow_assent.phoenix.gen.templates_test.exs b/test/mix/tasks/phoenix/pow_assent.phoenix.gen.templates_test.exs index 3ac37a3..f1e4ff2 100644 --- a/test/mix/tasks/phoenix/pow_assent.phoenix.gen.templates_test.exs +++ b/test/mix/tasks/phoenix/pow_assent.phoenix.gen.templates_test.exs @@ -39,8 +39,7 @@ defmodule Mix.Tasks.PowAssent.Phoenix.Gen.TemplatesTest do assert view_content =~ "defmodule PowAssentWeb.PowAssent.RegistrationView do" assert view_content =~ "use PowAssentWeb, :view" - for _ <- 1..2, do: assert_received({:mix_shell, :info, [_msg]}) - assert_received {:mix_shell, :info, [msg]} + assert_received {:mix_shell, :info, ["PowAssent Phoenix templates and views has been generated." <> msg]} assert msg =~ "defmodule PowAssentWeb.Endpoint" assert msg =~ "otp_app: :pow" assert msg =~ "repo: PowAssent.Repo" @@ -66,8 +65,7 @@ defmodule Mix.Tasks.PowAssent.Phoenix.Gen.TemplatesTest do assert view_content =~ "defmodule TestWeb.PowAssent.RegistrationView do" assert view_content =~ "use TestWeb, :view" - for _ <- 1..2, do: assert_received({:mix_shell, :info, [_msg]}) - assert_received {:mix_shell, :info, [msg]} + assert_received {:mix_shell, :info, ["PowAssent Phoenix templates and views has been generated." <> msg]} assert msg =~ "defmodule TestWeb.Endpoint" assert msg =~ "otp_app: :test" assert msg =~ "repo: Test.Repo" diff --git a/test/mix/tasks/pow_assent.install_test.exs b/test/mix/tasks/pow_assent.install_test.exs new file mode 100644 index 0000000..7f0b156 --- /dev/null +++ b/test/mix/tasks/pow_assent.install_test.exs @@ -0,0 +1,40 @@ +defmodule Mix.Tasks.PowAssent.InstallTest do + use PowAssent.Test.Mix.TestCase + + alias Mix.Tasks.PowAssent.Install + + @tmp_path Path.join(["tmp", inspect(Install)]) + + setup do + File.rm_rf!(@tmp_path) + File.mkdir_p!(@tmp_path) + + :ok + end + + test "runs" do + File.cd!(@tmp_path, fn -> + Install.run([]) + end) + end + + test "raises error in umbrella app" do + File.cd!(@tmp_path, fn -> + File.write!("mix.exs", """ + defmodule Umbrella.MixProject do + use Mix.Project + + def project do + [apps_path: "apps"] + end + end + """) + + Mix.Project.in_project(:umbrella, ".", fn _ -> + assert_raise Mix.Error, "mix pow_assent.install can't be used in umbrella apps. Run mix pow_assent.ecto.install in your ecto app directory.", fn -> + Install.run([]) + end + end) + end) + end +end diff --git a/test/pow_assent/ecto/schema_test.exs b/test/pow_assent/ecto/schema_test.exs index 9cfb587..785a0ce 100644 --- a/test/pow_assent/ecto/schema_test.exs +++ b/test/pow_assent/ecto/schema_test.exs @@ -18,7 +18,7 @@ defmodule PowAssent.Ecto.SchemaTest do use PowAssent.Test.Ecto.TestCase doctest PowAssent.Ecto.Schema - alias PowAssent.Test.Ecto.{Repo, Users.EmailConfirmUser, Users.User} + alias PowAssent.Test.Ecto.{Repo, Users.UserConfirmEmail, Users.User} test "user_schema/1" do user = %User{} @@ -61,10 +61,10 @@ defmodule PowAssent.Ecto.SchemaTest do end test "sets :email_confirmed_at when provided as attrs" do - changeset = EmailConfirmUser.user_identity_changeset(%EmailConfirmUser{}, @user_identity, %{}, %{email: "test@example.com"}) + changeset = UserConfirmEmail.user_identity_changeset(%UserConfirmEmail{}, @user_identity, %{}, %{email: "test@example.com"}) refute changeset.changes[:email_confirmed_at] - changeset = EmailConfirmUser.user_identity_changeset(%EmailConfirmUser{}, @user_identity, %{email: "test@example.com"}, %{}) + changeset = UserConfirmEmail.user_identity_changeset(%UserConfirmEmail{}, @user_identity, %{email: "test@example.com"}, %{}) assert changeset.changes[:email_confirmed_at] diff --git a/test/pow_assent/ecto/user_identities/context_test.exs b/test/pow_assent/ecto/user_identities/context_test.exs index c4170a0..1c90e11 100644 --- a/test/pow_assent/ecto/user_identities/context_test.exs +++ b/test/pow_assent/ecto/user_identities/context_test.exs @@ -1,10 +1,21 @@ +defmodule PowAssent.Test.Ecto.Users.UserWithoutUserIdentities do + @moduledoc false + use Ecto.Schema + use Pow.Ecto.Schema + + schema "users" do + pow_user_fields() + timestamps() + end +end + defmodule PowAssent.Ecto.UserIdentities.ContextTest do use PowAssent.Test.Ecto.TestCase doctest PowAssent.Ecto.UserIdentities.Context alias Ecto.Changeset alias PowAssent.Ecto.UserIdentities.Context - alias PowAssent.Test.Ecto.{Repo, Users.User} + alias PowAssent.Test.Ecto.{Repo, Users.User, Users.UserWithoutUserIdentities} @config [repo: Repo, user: User] @@ -28,6 +39,12 @@ defmodule PowAssent.Ecto.UserIdentities.ContextTest do {:ok, _user} = Repo.delete(user) refute Context.get_user_by_provider_uid("test_provider", "1", @config) end + + test "requires user has :user_identities assoc" do + assert_raise PowAssent.Config.ConfigError, "The `:user` configuration option doesn't have a `:user_identities` association.", fn -> + Context.get_user_by_provider_uid("test_provider", "2", repo: Repo, user: UserWithoutUserIdentities) + end + end end describe "create/5" do @@ -78,7 +95,7 @@ defmodule PowAssent.Ecto.UserIdentities.ContextTest do end test "when user id field is missing" do - assert {:error, {:missing_user_id_field, _changeset}} = Context.create_user("test_provider", "1", Map.delete(@valid_params, :email), %{}, @config) + assert {:error, {:invalid_user_id_field, _changeset}} = Context.create_user("test_provider", "1", Map.delete(@valid_params, :email), %{}, @config) end end diff --git a/test/pow_assent/ecto/user_identities/schema_test.exs b/test/pow_assent/ecto/user_identities/schema_test.exs index e82f5f9..c751a09 100644 --- a/test/pow_assent/ecto/user_identities/schema_test.exs +++ b/test/pow_assent/ecto/user_identities/schema_test.exs @@ -1,9 +1,22 @@ +module_raised_with = + try do + defmodule PowAssent.Test.UserIdentities.InvalidUserIdentity do + use PowAssent.Ecto.UserIdentities.Schema + end + rescue + e in PowAssent.Config.ConfigError -> e.message + end + defmodule PowAssent.Ecto.UserIdentities.SchemaTest do use PowAssent.Test.Ecto.TestCase doctest PowAssent.Ecto.UserIdentities.Schema alias PowAssent.Test.Ecto.{Repo, UserIdentities.UserIdentity, Users.User} + test "raises error during compile when there's no `:user` configuration" do + assert unquote(module_raised_with) =~ "No :user configuration option found for user identity schema module." + end + test "pow_assent_user_identity_fields/1" do user = %UserIdentity{} diff --git a/test/pow_assent/http_adapter/httpc_test.exs b/test/pow_assent/http_adapter/httpc_test.exs index 06ba0f6..8e7b884 100644 --- a/test/pow_assent/http_adapter/httpc_test.exs +++ b/test/pow_assent/http_adapter/httpc_test.exs @@ -6,13 +6,24 @@ defmodule PowAssent.HTTPAdapter.HttpcTest do @expired_certificate_url "https://expired.badssl.com" @hsts_certificate_url "https://hsts.badssl.com" + @unreachable_http_url "http://localhost:8888/" + @expired_certificate_error [ + {:to_address, {'expired.badssl.com', 443}}, + {:inet, [:inet], {:tls_alert, 'certificate expired'}} + ] + @unreachable_http_error [ + {:to_address, {'localhost', 8888}}, + {:inet, [:inet], :econnrefused} + ] describe "request/4" do test "handles SSL" do assert {:ok, %HTTPResponse{status: 200}} = Httpc.request(:get, @hsts_certificate_url, nil, []) - assert {:error, :econnrefused} = Httpc.request(:get, @expired_certificate_url, nil, []) + assert {:error, {:failed_connect, @expired_certificate_error}} = Httpc.request(:get, @expired_certificate_url, nil, []) assert {:ok, %HTTPResponse{status: 200}} = Httpc.request(:get, @expired_certificate_url, nil, [], ssl: []) + + assert {:error, {:failed_connect, @unreachable_http_error}} = Httpc.request(:get, @unreachable_http_url, nil, []) end end end diff --git a/test/pow_assent/http_adapter/mint_test.exs b/test/pow_assent/http_adapter/mint_test.exs index 5393cc2..6f7184a 100644 --- a/test/pow_assent/http_adapter/mint_test.exs +++ b/test/pow_assent/http_adapter/mint_test.exs @@ -6,13 +6,16 @@ defmodule PowAssent.HTTPAdapter.MintTest do @expired_certificate_url "https://expired.badssl.com" @hsts_certificate_url "https://hsts.badssl.com" + @unreachable_http_url "http://localhost:8888/" describe "request/4" do test "handles SSL" do assert {:ok, %HTTPResponse{status: 200}} = Mint.request(:get, @hsts_certificate_url, nil, []) - assert {:error, :econnrefused} = Mint.request(:get, @expired_certificate_url, nil, []) + assert {:error, {:tls_alert, 'certificate expired'}} = Mint.request(:get, @expired_certificate_url, nil, []) assert {:ok, %HTTPResponse{status: 200}} = Mint.request(:get, @expired_certificate_url, nil, [], transport_opts: [verify: :verify_none]) + + assert {:error, :econnrefused} = Mint.request(:get, @unreachable_http_url, nil, []) end end end diff --git a/test/pow_assent/phoenix/controllers/authorization_controller_test.exs b/test/pow_assent/phoenix/controllers/authorization_controller_test.exs index 2b326e3..5300bf2 100644 --- a/test/pow_assent/phoenix/controllers/authorization_controller_test.exs +++ b/test/pow_assent/phoenix/controllers/authorization_controller_test.exs @@ -2,7 +2,7 @@ defmodule PowAssent.Phoenix.AuthorizationControllerTest do use PowAssent.Test.Phoenix.ConnCase import PowAssent.OAuthHelpers - alias PowAssent.Test.Ecto.Users.User + alias PowAssent.Test.{Ecto.Users.User, UserIdentitiesMock} @provider "test_provider" @callback_params %{code: "test", redirect_uri: ""} @@ -12,7 +12,7 @@ defmodule PowAssent.Phoenix.AuthorizationControllerTest do setup_oauth2_strategy_env(server) - {:ok, conn: conn, user: %User{id: 1}, server: server} + {:ok, conn: conn, user: %User{id: :loaded}, server: server} end describe "GET /auth/:provider/new" do @@ -33,7 +33,7 @@ defmodule PowAssent.Phoenix.AuthorizationControllerTest do describe "GET /auth/:provider/callback with current user session" do test "adds identity", %{conn: conn, server: server, user: user} do - expect_oauth2_flow(server) + expect_oauth2_flow(server, user: %{uid: "new_identity"}) conn = conn @@ -46,11 +46,11 @@ defmodule PowAssent.Phoenix.AuthorizationControllerTest do end test "with identity bound to another user", %{conn: conn, server: server, user: user} do - expect_oauth2_flow(server, user: %{uid: "duplicate"}) + expect_oauth2_flow(server, user: %{uid: "new_identity"}) conn = conn - |> Pow.Plug.assign_current_user(user, []) + |> Pow.Plug.assign_current_user(%{user | id: :bound_to_different_user}, []) |> get(Routes.pow_assent_authorization_path(conn, :callback, @provider, @callback_params)) assert redirected_to(conn) == Routes.pow_registration_path(conn, :new) @@ -60,7 +60,7 @@ defmodule PowAssent.Phoenix.AuthorizationControllerTest do describe "GET /auth/:provider/callback as authentication" do test "with valid params", %{conn: conn, server: server} do - expect_oauth2_flow(server, user: %{uid: "existing"}) + expect_oauth2_flow(server, user: %{uid: "existing_user"}) conn = get conn, Routes.pow_assent_authorization_path(conn, :callback, @provider, @callback_params) @@ -70,9 +70,9 @@ defmodule PowAssent.Phoenix.AuthorizationControllerTest do describe "GET /auth/:provider/callback as authentication with email confirmation" do test "with missing e-mail confirmation", %{conn: conn, server: server} do - expect_oauth2_flow(server, user: %{uid: "user-missing-email-confirmation"}) + expect_oauth2_flow(server, user: %{uid: "new_user-missing_email_confirmation"}) - conn = Phoenix.ConnTest.dispatch conn, PowAssent.Test.Phoenix.MailerEndpoint, :get, Routes.pow_assent_authorization_path(conn, :callback, @provider, @callback_params) + conn = Phoenix.ConnTest.dispatch conn, PowAssent.Test.Phoenix.EndpointConfirmEmail, :get, Routes.pow_assent_authorization_path(conn, :callback, @provider, @callback_params) refute Pow.Plug.current_user(conn) @@ -86,19 +86,19 @@ defmodule PowAssent.Phoenix.AuthorizationControllerTest do describe "GET /auth/:provider/callback as registration" do test "with valid params", %{conn: conn, server: server} do - expect_oauth2_flow(server, user: %{email: "newuser@example.com"}) + expect_oauth2_flow(server, user: %{uid: "new_user"}) conn = get conn, Routes.pow_assent_authorization_path(conn, :callback, @provider, @callback_params) assert redirected_to(conn) == "/registration_created" assert user = Pow.Plug.current_user(conn) assert [user_identity] = user.user_identities - assert user_identity.uid == "1" + assert user_identity.uid == "new_user" assert user_identity.provider == "test_provider" end test "with missing params", %{conn: conn, server: server} do - expect_oauth2_flow(server, user: %{email: "newuser@example.com", name: ""}) + expect_oauth2_flow(server, user: %{uid: "new_user", name: ""}) conn = get conn, Routes.pow_assent_authorization_path(conn, :callback, @provider, @callback_params) @@ -107,12 +107,12 @@ defmodule PowAssent.Phoenix.AuthorizationControllerTest do end test "with missing required user id", %{conn: conn, server: server} do - expect_oauth2_flow(server) + expect_oauth2_flow(server, user: %{"email" => ""}) conn = get conn, Routes.pow_assent_authorization_path(conn, :callback, @provider, @callback_params) assert redirected_to(conn) == Routes.pow_assent_registration_path(conn, :add_user_id, "test_provider") - assert Plug.Conn.get_session(conn, "pow_assent_params") == %{"name" => "Dan Schultzer", "uid" => "1"} + assert Plug.Conn.get_session(conn, "pow_assent_params") == %{"name" => "Dan Schultzer", "uid" => "new_user", "email" => ""} end test "with an existing required user id", %{conn: conn, server: server} do @@ -121,7 +121,7 @@ defmodule PowAssent.Phoenix.AuthorizationControllerTest do conn = get conn, Routes.pow_assent_authorization_path(conn, :callback, @provider, @callback_params) assert redirected_to(conn) == Routes.pow_assent_registration_path(conn, :add_user_id, "test_provider") - assert Plug.Conn.get_session(conn, "pow_assent_params") == %{"email" => "taken@example.com", "name" => "Dan Schultzer", "uid" => "1"} + assert Plug.Conn.get_session(conn, "pow_assent_params") == %{"email" => "taken@example.com", "name" => "Dan Schultzer", "uid" => "new_user"} end end @@ -149,7 +149,7 @@ defmodule PowAssent.Phoenix.AuthorizationControllerTest do end test "with same state", %{conn: conn, server: server} do - expect_oauth2_flow(server) + expect_oauth2_flow(server, user: %{"email" => ""}) conn = conn @@ -163,51 +163,33 @@ defmodule PowAssent.Phoenix.AuthorizationControllerTest do test "with timeout", %{conn: conn, server: server} do Bypass.down(server) - assert_raise RuntimeError, "Connection refused", fn -> + message = ~r/Server was unreachable with PowAssent.HTTPAdapter.Httpc/ + + assert_raise PowAssent.RequestError, message, fn -> get conn, Routes.pow_assent_authorization_path(conn, :callback, @provider, @callback_params) end end end describe "DELETE /auth/:provider" do - test "with no user password", %{conn: conn} do + test "when requires a user password set", %{conn: conn} do conn = conn - |> Pow.Plug.assign_current_user(%User{id: :with_user_identity}, []) + |> Pow.Plug.assign_current_user(:no_password, []) |> delete(Routes.pow_assent_authorization_path(conn, :delete, @provider)) assert redirected_to(conn) == Routes.pow_registration_path(conn, :edit) assert get_flash(conn, :error) == "Authentication cannot be removed until you've entered a password for your account." end - test "with two identities", %{conn: conn} do - conn = - conn - |> Pow.Plug.assign_current_user(%User{id: :with_two_user_identities}, []) - |> delete(Routes.pow_assent_authorization_path(conn, :delete, @provider)) - - assert redirected_to(conn) == Routes.pow_registration_path(conn, :edit) - assert get_flash(conn, :info) == "Authentication with Test provider has been removed" - end - - test "with user password", %{conn: conn} do + test "when can be deleted", %{conn: conn} do conn = conn - |> Pow.Plug.assign_current_user(%User{id: :with_user_identity, password_hash: :set}, []) + |> Pow.Plug.assign_current_user(UserIdentitiesMock.user(), []) |> delete(Routes.pow_assent_authorization_path(conn, :delete, @provider)) assert redirected_to(conn) == Routes.pow_registration_path(conn, :edit) assert get_flash(conn, :info) == "Authentication with Test provider has been removed" end - - test "with current_user session without provider", %{conn: conn, user: user} do - conn = - conn - |> Pow.Plug.assign_current_user(user, []) - |> delete(Routes.pow_assent_authorization_path(conn, :delete, @provider)) - - assert redirected_to(conn) == Routes.pow_registration_path(conn, :edit) - assert get_flash(conn, :error) == "Authentication cannot be removed until you've entered a password for your account." - end end end diff --git a/test/pow_assent/phoenix/controllers/registration_controller_test.exs b/test/pow_assent/phoenix/controllers/registration_controller_test.exs index 3805962..cd84c2b 100644 --- a/test/pow_assent/phoenix/controllers/registration_controller_test.exs +++ b/test/pow_assent/phoenix/controllers/registration_controller_test.exs @@ -2,9 +2,10 @@ defmodule PowAssent.Phoenix.RegistrationControllerTest do use PowAssent.Test.Phoenix.ConnCase @provider "test_provider" + @provider_params %{"uid" => "new_user", "name" => "John Doe"} setup %{conn: conn} do - conn = Plug.Conn.put_session(conn, :pow_assent_params, %{"uid" => "1", "name" => "John Doe"}) + conn = Plug.Conn.put_session(conn, :pow_assent_params, @provider_params) {:ok, conn: conn} end @@ -46,22 +47,16 @@ defmodule PowAssent.Phoenix.RegistrationControllerTest do assert get_flash(conn, :info) == "user_created_test_provider" end - test "with already taken user id field", %{conn: conn} do + test "with error", %{conn: conn} do conn = post conn, Routes.pow_assent_registration_path(conn, :create, @provider), %{user: %{email: "taken@example.com"}} assert html_response(conn, 200) =~ "has already been taken" end - test " with invalid user id field (email)", %{conn: conn} do - conn = post conn, Routes.pow_assent_registration_path(conn, :create, @provider), %{user: %{email: "foo"}} - - assert html_response(conn, 200) =~ "has invalid format" - end - test "with duplicate identity", %{conn: conn} do conn = conn - |> Plug.Conn.put_session(:pow_assent_params, %{"uid" => "duplicate", "name" => "John Doe"}) + |> Plug.Conn.put_session(:pow_assent_params, %{"uid" => "different_user", "name" => "John Doe"}) |> post(Routes.pow_assent_registration_path(conn, :create, @provider), %{user: %{email: "foo@example.com"}}) assert redirected_to(conn) == "/logged-out" @@ -70,8 +65,8 @@ defmodule PowAssent.Phoenix.RegistrationControllerTest do end describe "GET /auth/:provider/create with PowEmailConfirmation" do - test "with valid", %{conn: conn} do - conn = Phoenix.ConnTest.dispatch conn, PowAssent.Test.Phoenix.MailerEndpoint, :post, Routes.pow_assent_registration_path(conn, :create, @provider), %{user: %{email: "foo@example.com"}} + test "with user entered email", %{conn: conn} do + conn = Phoenix.ConnTest.dispatch conn, PowAssent.Test.Phoenix.EndpointConfirmEmail, :post, Routes.pow_assent_registration_path(conn, :create, @provider), %{user: %{email: "foo@example.com"}} assert redirected_to(conn) == "/registration_created" assert get_flash(conn, :info) == "user_created_test_provider" @@ -86,8 +81,8 @@ defmodule PowAssent.Phoenix.RegistrationControllerTest do test "with email from provider", %{conn: conn} do conn = conn - |> Plug.Conn.put_session(:pow_assent_params, %{"uid" => "1", "name" => "John Doe", "email" => "foo@example.com"}) - |> Phoenix.ConnTest.dispatch(PowAssent.Test.Phoenix.MailerEndpoint, :post, Routes.pow_assent_registration_path(conn, :create, @provider), %{user: %{}}) + |> Plug.Conn.put_session(:pow_assent_params, %{"uid" => "new_user", "name" => "John Doe", "email" => "foo@example.com"}) + |> Phoenix.ConnTest.dispatch(PowAssent.Test.Phoenix.EndpointConfirmEmail, :post, Routes.pow_assent_registration_path(conn, :create, @provider), %{user: %{}}) assert redirected_to(conn) == "/registration_created" diff --git a/test/pow_assent/phoenix/views/view_helpers_test.exs b/test/pow_assent/phoenix/views/view_helpers_test.exs index 1108fa2..7766448 100644 --- a/test/pow_assent/phoenix/views/view_helpers_test.exs +++ b/test/pow_assent/phoenix/views/view_helpers_test.exs @@ -4,12 +4,12 @@ defmodule PowAssent.ViewHelpersTest do alias Plug.Conn alias Phoenix.HTML.Link alias PowAssent.Phoenix.ViewHelpers - alias PowAssent.Test.Ecto.Users.User + alias PowAssent.Test.{Phoenix.Router, UserIdentitiesMock} setup %{conn: conn} do config = [ - repo: PowAssent.Test.Phoenix.MockRepo, pow_assent: [ + user_identities_context: UserIdentitiesMock, providers: [ test_provider: [ strategy: TestProvider @@ -21,7 +21,7 @@ defmodule PowAssent.ViewHelpersTest do conn = conn |> Conn.put_private(:pow_config, config) - |> Conn.put_private(:phoenix_router, PowAssent.Test.Phoenix.Router) + |> Conn.put_private(:phoenix_router, Router) {:ok, conn: conn} end @@ -30,7 +30,7 @@ defmodule PowAssent.ViewHelpersTest do [safe: iodata] = ViewHelpers.provider_links(conn) assert {:safe, iodata} == Link.link("Sign in with Test provider", to: "/auth/test_provider/new") - conn = Pow.Plug.assign_current_user(conn, %User{id: 1}, []) + conn = Pow.Plug.assign_current_user(conn, UserIdentitiesMock.user(), []) [safe: iodata] = ViewHelpers.provider_links(conn) assert {:safe, iodata} == Link.link("Remove Test provider authentication", to: "/auth/test_provider", method: "delete") diff --git a/test/pow_assent/plug_test.exs b/test/pow_assent/plug_test.exs index c0d97bd..dac0b13 100644 --- a/test/pow_assent/plug_test.exs +++ b/test/pow_assent/plug_test.exs @@ -4,14 +4,16 @@ defmodule PowAssent.PlugTest do alias Plug.{ProcessStore, Session, Test} alias PowAssent.Plug - alias PowAssent.Test.{ContextMock, Ecto.Users.User} + alias PowAssent.Test.{UserIdentitiesMock, Ecto.Users.User} import PowAssent.OAuthHelpers @default_config [ mod: Pow.Plug.Session, - user_identities_context: ContextMock, user: User, - otp_app: :pow_assent + otp_app: :pow_assent, + pow_assent: [ + user_identities_context: UserIdentitiesMock + ] ] defp setup_conn do @@ -54,7 +56,7 @@ defmodule PowAssent.PlugTest do end test "creates user identity", %{conn: conn, server: server} do - user = %{ContextMock.user() | id: :loaded} + user = %{UserIdentitiesMock.user() | id: :loaded} conn = Pow.Plug.assign_current_user(conn, user, @default_config) expect_oauth2_flow(server, user: %{uid: "new_identity"}) @@ -65,7 +67,7 @@ defmodule PowAssent.PlugTest do end test "already taken user identity", %{conn: conn, server: server} do - conn = Pow.Plug.assign_current_user(conn, %{ContextMock.user() | id: :bound_to_different_user}, @default_config) + conn = Pow.Plug.assign_current_user(conn, %{UserIdentitiesMock.user() | id: :bound_to_different_user}, @default_config) expect_oauth2_flow(server, user: %{uid: "new_identity"}) @@ -86,7 +88,7 @@ defmodule PowAssent.PlugTest do expect_oauth2_flow(server, user: %{uid: "new_user", email: ""}) assert {:ok, url, _conn} = Plug.authenticate(conn, "test_provider", "https://example.com/") - assert {:error, {:missing_user_id_field, %{}}, conn} = Plug.callback(conn, "test_provider", %{"code" => "access_token", "redirect_uri" => url}) + assert {:error, {:invalid_user_id_field, %{}}, conn} = Plug.callback(conn, "test_provider", %{"code" => "access_token", "redirect_uri" => url}) refute conn.private[:plug_session]["pow_assent_auth"] end end @@ -114,7 +116,7 @@ defmodule PowAssent.PlugTest do setup do conn = setup_conn() - conn = Pow.Plug.assign_current_user(conn, ContextMock.user(), @default_config) + conn = Pow.Plug.assign_current_user(conn, UserIdentitiesMock.user(), @default_config) {:ok, conn: conn} end @@ -133,7 +135,7 @@ defmodule PowAssent.PlugTest do setup do conn = setup_conn() - conn = Pow.Plug.assign_current_user(conn, ContextMock.user(), @default_config) + conn = Pow.Plug.assign_current_user(conn, UserIdentitiesMock.user(), @default_config) {:ok, conn: conn} end diff --git a/test/pow_assent/strategies/azure_oauth2.exs b/test/pow_assent/strategies/azure_oauth2_test.exs similarity index 92% rename from test/pow_assent/strategies/azure_oauth2.exs rename to test/pow_assent/strategies/azure_oauth2_test.exs index 5ee58ef..5db6838 100644 --- a/test/pow_assent/strategies/azure_oauth2.exs +++ b/test/pow_assent/strategies/azure_oauth2_test.exs @@ -1,4 +1,4 @@ -defmodule PowAssent.Strategy.AzureOAuth2 do +defmodule PowAssent.Strategy.AzureOAuth2Test do use PowAssent.Test.Phoenix.ConnCase import PowAssent.OAuthHelpers @@ -30,9 +30,9 @@ defmodule PowAssent.Strategy.AzureOAuth2 do expected = %{ "uid" => "JWvYdCWPhhlpS1Zsf7yYUxShUwtUm5yzPmw_-jX3fHY", "name" => "Frank Miller", - "given_name" => "Frank", - "family_name" => "Miller", - "email" => "frank@contoso.com", + "first_name" => "Frank", + "last_name" => "Miller", + "email" => "frankm@contoso.com" } {:ok, %{user: user}} = AzureOAuth2.callback(config, conn, params) diff --git a/test/pow_assent/strategies/facebook_test.exs b/test/pow_assent/strategies/facebook_test.exs index be0d832..9f955d6 100644 --- a/test/pow_assent/strategies/facebook_test.exs +++ b/test/pow_assent/strategies/facebook_test.exs @@ -56,8 +56,7 @@ defmodule PowAssent.Strategy.FacebookTest do test "handles error", %{config: config, conn: conn, params: params} do config = Keyword.put(config, :site, "http://localhost:8888") - assert {:error, %{conn: %Plug.Conn{}, error: error}} = Facebook.callback(config, conn, params) - assert error == :econnrefused + assert {:error, %{conn: %Plug.Conn{}, error: %PowAssent.RequestError{error: :unreachable}}} = Facebook.callback(config, conn, params) end end end diff --git a/test/pow_assent/strategies/instagram_test.exs b/test/pow_assent/strategies/instagram_test.exs index d93aebc..5a4b80c 100644 --- a/test/pow_assent/strategies/instagram_test.exs +++ b/test/pow_assent/strategies/instagram_test.exs @@ -42,8 +42,7 @@ defmodule PowAssent.Strategy.InstagramTest do test "handles error", %{config: config, conn: conn, params: params} do config = Keyword.put(config, :site, "http://localhost:8888") - assert {:error, %{conn: %Plug.Conn{}, error: error}} = Instagram.callback(config, conn, params) - assert error == :econnrefused + assert {:error, %{conn: %Plug.Conn{}, error: %PowAssent.RequestError{error: :unreachable}}} = Instagram.callback(config, conn, params) end end end diff --git a/test/pow_assent/strategies/oauth2_test.exs b/test/pow_assent/strategies/oauth2_test.exs index e41d62d..acb3326 100644 --- a/test/pow_assent/strategies/oauth2_test.exs +++ b/test/pow_assent/strategies/oauth2_test.exs @@ -2,7 +2,7 @@ defmodule PowAssent.Strategy.OAuth2Test do use PowAssent.Test.Phoenix.ConnCase import PowAssent.OAuthHelpers - alias PowAssent.{ConfigurationError, RequestError, Strategy.OAuth2} + alias PowAssent.{ConfigurationError, CallbackError, RequestError, Strategy.OAuth2} setup :setup_bypass @@ -45,6 +45,12 @@ defmodule PowAssent.Strategy.OAuth2Test do assert user == %{"email" => "foo@example.com", "name" => "Dan Schultzer", "uid" => "1"} end + test "with redirect error", %{conn: conn, config: config} do + params = %{"error" => "access_denied", "error_description" => "The user denied the request", "state" => "test"} + + assert {:error, %{conn: _conn, error: %CallbackError{message: "The user denied the request", error: "access_denied", error_uri: nil}}} = OAuth2.callback(config, conn, params) + end + test "access token error with 200 response", %{conn: conn, config: config, params: params, bypass: bypass} do expect_oauth2_access_token_request(bypass, params: %{"error" => "error", "error_description" => "Error description"}) @@ -70,7 +76,7 @@ defmodule PowAssent.Strategy.OAuth2Test do expect_oauth2_access_token_request(bypass) - assert {:error, %{conn: _conn, error: :econnrefused}} = OAuth2.callback(config, conn, params) + assert {:error, %{conn: _conn, error: %PowAssent.RequestError{error: :unreachable}}} = OAuth2.callback(config, conn, params) end test "user url unauthorized access token", %{conn: conn, config: config, params: params, bypass: bypass} do diff --git a/test/pow_assent/strategies/oauth_test.exs b/test/pow_assent/strategies/oauth_test.exs index 2381adf..2c52d07 100644 --- a/test/pow_assent/strategies/oauth_test.exs +++ b/test/pow_assent/strategies/oauth_test.exs @@ -43,7 +43,7 @@ defmodule PowAssent.Strategy.OAuthTest do test "bubbles up network error", %{conn: conn, config: config, bypass: bypass} do Bypass.down(bypass) - assert {:error, %{conn: _conn, error: :econnrefused}} = OAuth.authorize_url(config, conn) + assert {:error, %{conn: _conn, error: %PowAssent.RequestError{error: :unreachable}}} = OAuth.authorize_url(config, conn) end end diff --git a/test/pow_assent/strategies/vk_test.exs b/test/pow_assent/strategies/vk_test.exs index 14b43f5..06e5b73 100644 --- a/test/pow_assent/strategies/vk_test.exs +++ b/test/pow_assent/strategies/vk_test.exs @@ -72,8 +72,7 @@ defmodule PowAssent.Strategy.VKTest do test "handles error", %{config: config, conn: conn, params: params} do config = Keyword.put(config, :site, "http://localhost:8888") - assert {:error, %{conn: %Plug.Conn{}, error: error}} = VK.callback(config, conn, params) - assert error == :econnrefused + assert {:error, %{conn: %Plug.Conn{}, error: %PowAssent.RequestError{error: :unreachable}}} = VK.callback(config, conn, params) end end end diff --git a/test/pow_assent/strategy_test.exs b/test/pow_assent/strategy_test.exs index 5dc5446..e31a410 100644 --- a/test/pow_assent/strategy_test.exs +++ b/test/pow_assent/strategy_test.exs @@ -21,6 +21,9 @@ defmodule PowAssent.StrategyTest do headers = [{"content-type", "application/json; charset=utf-8"}] assert Strategy.decode_response({nil, %{body: body, headers: headers}}, []) == {nil, %{body: expected, headers: headers}} + headers = [{"content-type", "text/javascript"}] + assert Strategy.decode_response({nil, %{body: body, headers: headers}}, []) == {nil, %{body: expected, headers: headers}} + headers = [{"content-type", "application/x-www-form-urlencoded"}] body = URI.encode_query(expected) assert Strategy.decode_response({nil, %{body: body, headers: headers}}, []) == {nil, %{body: expected, headers: headers}} diff --git a/test/support/context_mock.ex b/test/support/context_mock.ex deleted file mode 100644 index 9dcfe00..0000000 --- a/test/support/context_mock.ex +++ /dev/null @@ -1,25 +0,0 @@ -defmodule PowAssent.Test.ContextMock do - @moduledoc false - use PowAssent.Ecto.UserIdentities.Context - alias PowAssent.Test.Ecto.{UserIdentities.UserIdentity, Users.User} - - @user %User{id: 1, email: "test@example.com", password_hash: ""} - @user_identity %UserIdentity{provider: "test_provider", uid: "1"} - - def get_user_by_provider_uid("test_provider", "existing_user"), do: @user - def get_user_by_provider_uid("test_provider", "new_user"), do: nil - - def create(%User{id: :loaded}, "test_provider", "new_identity"), do: {:ok, :new_identity} - def create(%User{id: :bound_to_different_user}, "test_provider", "new_identity"), do: {:error, {:bound_to_different_user, %{}}} - - def create_user("test_provider", "new_user", %{"email" => ""}, _user_id_params), do: {:error, {:missing_user_id_field, %{}}} - def create_user("test_provider", "new_user", _params, _user_id_params), do: {:ok, %{@user | id: :new_user}} - def create_user("test_provider", "different_user", _params, _user_id_params), do: {:error, {:bound_to_different_user, %{}}} - - def delete(@user, "test_provider"), do: {:ok, {1, nil}} - def delete(:error, "test_provider"), do: {:error, :error} - - def all(@user), do: [@user_identity] - - def user, do: @user -end diff --git a/test/support/ecto/fixture.ex b/test/support/ecto/fixture.ex deleted file mode 100644 index b2ce756..0000000 --- a/test/support/ecto/fixture.ex +++ /dev/null @@ -1,20 +0,0 @@ -# defmodule PowAssent.Test.Ecto.Fixture do -# alias PowAssent.Test.Ecto.Repo -# alias PowAssent.Test.Ecto.Users.User -# # alias PowAssent.UserIdentities.UserIdentity - -# def user(attrs \\ %{}) do -# %User{} -# |> Map.merge(%{email: "user@example.com"}) -# |> Map.merge(attrs) -# |> Repo.insert!() -# end -# # def user_identity(user, attrs) do -# # {:ok, identity} = %UserIdentity{user: user} -# # |> Map.merge(%{provider: "test_provider", uid: "1"}) -# # |> Map.merge(attrs) -# # |> Repo.insert - -# # identity -# # end -# end diff --git a/test/support/ecto/email_confirm_user.ex b/test/support/ecto/user_confirm_email.ex similarity index 81% rename from test/support/ecto/email_confirm_user.ex rename to test/support/ecto/user_confirm_email.ex index f1b2a3a..4ca0585 100644 --- a/test/support/ecto/email_confirm_user.ex +++ b/test/support/ecto/user_confirm_email.ex @@ -1,4 +1,4 @@ -defmodule PowAssent.Test.Ecto.Users.EmailConfirmUser do +defmodule PowAssent.Test.Ecto.Users.UserConfirmEmail do @moduledoc false use Ecto.Schema use Pow.Ecto.Schema @@ -8,7 +8,7 @@ defmodule PowAssent.Test.Ecto.Users.EmailConfirmUser do schema "users" do has_many :user_identities, - PowAssent.Test.Ecto.UserIdentities.EmailConfirmUserIdentity, + PowAssent.Test.Ecto.UserIdentities.UserIdentityConfirmEmail, on_delete: :delete_all, foreign_key: :user_id diff --git a/test/support/ecto/email_confirm_user_identity.ex b/test/support/ecto/user_identity_confirm_email.ex similarity index 55% rename from test/support/ecto/email_confirm_user_identity.ex rename to test/support/ecto/user_identity_confirm_email.ex index 8fdb4d4..37612ab 100644 --- a/test/support/ecto/email_confirm_user_identity.ex +++ b/test/support/ecto/user_identity_confirm_email.ex @@ -1,8 +1,8 @@ -defmodule PowAssent.Test.Ecto.UserIdentities.EmailConfirmUserIdentity do +defmodule PowAssent.Test.Ecto.UserIdentities.UserIdentityConfirmEmail do @moduledoc false use Ecto.Schema use PowAssent.Ecto.UserIdentities.Schema, - user: PowAssent.Test.Ecto.Users.EmailConfirmUser + user: PowAssent.Test.Ecto.Users.UserConfirmEmail schema "user_identities" do pow_assent_user_identity_fields() diff --git a/test/support/mix/test_case.ex b/test/support/mix/test_case.ex index 1502bc4..7d5cc56 100644 --- a/test/support/mix/test_case.ex +++ b/test/support/mix/test_case.ex @@ -8,5 +8,17 @@ defmodule PowAssent.Test.Mix.TestCase do :ok end + setup context do + current_shell = Mix.shell() + + on_exit fn -> + Mix.shell(current_shell) + end + + Mix.shell(Mix.Shell.Process) + + context + end + defp clear_tmp_files, do: File.rm_rf!("tmp") end diff --git a/test/support/oauth_helpers.ex b/test/support/oauth_helpers.ex index a41bba9..76b7114 100644 --- a/test/support/oauth_helpers.ex +++ b/test/support/oauth_helpers.ex @@ -119,19 +119,12 @@ defmodule PowAssent.OAuthHelpers do @spec expect_oauth2_flow(Bypass.t(), Keyword.t()) :: :ok def expect_oauth2_flow(bypass, opts \\ []) do token_params = Keyword.get(opts, :token, %{"access_token" => "access_token"}) - user_params = Map.merge(%{uid: "1", name: "Dan Schultzer"}, Keyword.get(opts, :user, %{})) + user_params = Map.merge(%{uid: "new_user", name: "Dan Schultzer"}, Keyword.get(opts, :user, %{})) expect_oauth2_access_token_request(bypass, params: token_params) expect_oauth2_user_request(bypass, user_params) end - @spec expect_once_oauth2_access_token(Bypass.t(), binary(), binary()) :: any() - def expect_once_oauth2_access_token(bypass, uri, access_token) do - Bypass.expect_once(bypass, "POST", uri, fn conn -> - send_json_resp(conn, %{access_token: access_token}) - end) - end - defp assert_bearer_token_in_header(conn, token) do expected = {"authorization", "Bearer #{token}"} @@ -147,7 +140,7 @@ defmodule PowAssent.OAuthHelpers do end end - defp send_json_resp(conn, body, status_code \\ 200) do + defp send_json_resp(conn, body, status_code) do conn |> Conn.put_resp_content_type("application/json") |> Conn.send_resp(status_code, Jason.encode!(body)) diff --git a/test/support/phoenix/endpoint.ex b/test/support/phoenix/endpoint.ex index fb6245a..c73aeed 100644 --- a/test/support/phoenix/endpoint.ex +++ b/test/support/phoenix/endpoint.ex @@ -20,10 +20,12 @@ defmodule PowAssent.Test.Phoenix.Endpoint do plug Pow.Plug.Session, user: PowAssent.Test.Ecto.Users.User, - repo: PowAssent.Test.Phoenix.MockRepo, routes_backend: PowAssent.Test.Phoenix.Routes, messages_backend: PowAssent.Test.Phoenix.Messages, - otp_app: :pow_assent + otp_app: :pow_assent, + pow_assent: [ + user_identities_context: PowAssent.Test.UserIdentitiesMock + ] plug PowAssent.Test.Phoenix.Router end diff --git a/test/support/phoenix/mailer_endpoint.ex b/test/support/phoenix/endpoint_confirm_email.ex similarity index 71% rename from test/support/phoenix/mailer_endpoint.ex rename to test/support/phoenix/endpoint_confirm_email.ex index c9f8dc3..acd1ba5 100644 --- a/test/support/phoenix/mailer_endpoint.ex +++ b/test/support/phoenix/endpoint_confirm_email.ex @@ -1,4 +1,4 @@ -defmodule PowAssent.Test.Phoenix.MailerEndpoint do +defmodule PowAssent.Test.Phoenix.EndpointConfirmEmail do @moduledoc false use Phoenix.Endpoint, otp_app: :pow_assent @@ -19,12 +19,14 @@ defmodule PowAssent.Test.Phoenix.MailerEndpoint do signing_salt: "secret" plug Pow.Plug.Session, - user: PowAssent.Test.Ecto.Users.EmailConfirmUser, - repo: PowAssent.Test.Phoenix.MockRepo, + user: PowAssent.Test.Ecto.Users.UserConfirmEmail, routes_backend: PowAssent.Test.Phoenix.Routes, messages_backend: PowAssent.Test.Phoenix.Messages, mailer_backend: PowAssent.Test.Phoenix.MailerMock, - otp_app: :pow_assent + otp_app: :pow_assent, + pow_assent: [ + user_identities_context: PowAssent.Test.UserIdentitiesConfirmEmailMock + ] plug PowAssent.Test.Phoenix.Router end diff --git a/test/support/phoenix/mock_repo.ex b/test/support/phoenix/mock_repo.ex deleted file mode 100644 index 52fa363..0000000 --- a/test/support/phoenix/mock_repo.ex +++ /dev/null @@ -1,36 +0,0 @@ -defmodule PowAssent.Test.Phoenix.MockRepo do - @moduledoc false - alias Ecto.Changeset - alias PowAssent.Test.Ecto.UserIdentities.{EmailConfirmUserIdentity, UserIdentity} - alias PowAssent.Test.Ecto.Users.{EmailConfirmUser, User} - - @user_identity %UserIdentity{user_id: 1, id: 1, provider: "test_provider", uid: "1"} - @email_confirm_user_identity %EmailConfirmUserIdentity{user_id: 1, id: 1, provider: "test_provider", uid: "1"} - - def all(_), do: [@user_identity] - - def get_by(UserIdentity, provider: "test_provider", uid: "existing"), do: @user_identity - def get_by(EmailConfirmUserIdentity, provider: "test_provider", uid: "user-missing-email-confirmation"), do: Map.put(@email_confirm_user_identity, :id, :with_missing_email_confirmation) - def get_by(UserIdentity, provider: "test_provider", uid: "1"), do: nil - - def preload(%User{id: 1} = user, :user_identities, force: true), do: %{user | user_identities: []} - def preload(%User{id: :with_user_identity} = user, :user_identities, force: true), do: %{user | user_identities: [@user_identity]} - def preload(%User{id: :with_two_user_identities} = user, :user_identities, force: true), do: %{user | user_identities: [@user_identity, Map.put(@user_identity, :provider, "different_provider")]} - - def preload(%EmailConfirmUserIdentity{id: :with_missing_email_confirmation} = user_identity, :user), do: %{user_identity | user: %EmailConfirmUser{id: 1, email_confirmation_token: "token", email_confirmed_at: nil}} - def preload(%UserIdentity{id: 1} = user_identity, :user), do: %{user_identity | user: %User{id: 1}} - def preload(nil, :user), do: nil - - def insert(%{data: %UserIdentity{}, changes: %{uid: "duplicate"}} = changeset), do: {:error, %{Changeset.add_error(changeset, :uid_provider, "has already been taken", fields: [:provider, :uid]) | action: :insert}} - def insert(%{changes: %{user_identities: [%{changes: %{provider: "test_provider", uid: "duplicate"}} = user_identity_changeset]}} = changeset) do - user_identity_changeset = Changeset.add_error(user_identity_changeset, :uid_provider, "has already been taken", fields: [:provider, :uid]) - changeset = Changeset.change(changeset, user_identities: [user_identity_changeset]) - - {:error, changeset} - end - def insert(%{changes: %{email: "taken@example.com"}} = changeset), do: {:error, %{Changeset.add_error(changeset, :email, "has already been taken") | action: :insert}} - def insert(%{valid?: true} = changeset), do: {:ok, Changeset.apply_changes(changeset)} - def insert(%{valid?: false} = changeset), do: {:error, %{changeset | action: :insert}} - - def delete_all(_), do: {:ok, {1, nil}} -end diff --git a/test/support/user_identities_confirm_email_mock.ex b/test/support/user_identities_confirm_email_mock.ex new file mode 100644 index 0000000..58785a3 --- /dev/null +++ b/test/support/user_identities_confirm_email_mock.ex @@ -0,0 +1,12 @@ +defmodule PowAssent.Test.UserIdentitiesConfirmEmailMock do + @moduledoc false + use PowAssent.Ecto.UserIdentities.Context + alias PowAssent.Test.Ecto.Users.UserConfirmEmail + + @user %UserConfirmEmail{id: 1, email: "test@example.com", email_confirmation_token: "token"} + + def get_user_by_provider_uid("test_provider", "new_user-missing_email_confirmation"), do: %{@user | email_confirmed_at: nil} + + def create_user("test_provider", "new_user", _params, %{"email" => "foo@example.com"}), do: {:ok, %{@user | email: "foo@example.com"}} + def create_user("test_provider", "new_user", %{"email" => "foo@example.com"}, _user_params), do: {:ok, %{@user | email: "foo@example.com", email_confirmed_at: DateTime.utc_now()}} +end diff --git a/test/support/user_identities_mock.ex b/test/support/user_identities_mock.ex new file mode 100644 index 0000000..4985f3c --- /dev/null +++ b/test/support/user_identities_mock.ex @@ -0,0 +1,33 @@ +defmodule PowAssent.Test.UserIdentitiesMock do + @moduledoc false + use PowAssent.Ecto.UserIdentities.Context + alias PowAssent.Test.Ecto.{UserIdentities.UserIdentity, Users.User} + + @user %User{id: 1, email: "test@example.com", password_hash: ""} + @changeset_invalid_name %User{} |> Ecto.Changeset.cast(%{"name" => ""}, [:name]) |> Ecto.Changeset.validate_required([:name]) |> Map.put(:action, :create) + @changeset_taken_email %User{} |> Ecto.Changeset.cast(%{"email" => "taken@example.com"}, [:email]) |> Ecto.Changeset.add_error(:email, "has already been taken") |> Map.put(:action, :create) + @user_identity %UserIdentity{provider: "test_provider", uid: "1"} + @created_user_with_identity %{@user | id: :new_user, user_identities: [%{@user_identity | uid: "new_user"}]} + + def get_user_by_provider_uid("test_provider", "existing_user"), do: @user + def get_user_by_provider_uid("test_provider", "new_user"), do: nil + + def create(%User{id: :loaded}, "test_provider", "new_identity"), do: {:ok, :new_identity} + def create(%User{id: :bound_to_different_user}, "test_provider", "new_identity"), do: {:error, {:bound_to_different_user, %{}}} + + def create_user("test_provider", "new_user", %{"name" => ""}, _user_id_params), do: {:error, @changeset_invalid_name} + def create_user("test_provider", "new_user", %{"email" => ""}, _user_id_params), do: {:error, {:invalid_user_id_field, %{}}} + def create_user("test_provider", "new_user", %{"email" => "taken@example.com"}, _user_id_params), do: {:error, {:invalid_user_id_field, @changeset_taken_email}} + def create_user("test_provider", "new_user", _params, %{"email" => "foo@example.com"}), do: {:ok, %{@created_user_with_identity | email: "foo@example.com"}} + def create_user("test_provider", "new_user", _params, %{"email" => "taken@example.com"}), do: {:error, {:invalid_user_id_field, @changeset_taken_email}} + def create_user("test_provider", "new_user", _params, _user_id_params), do: {:ok, @created_user_with_identity} + def create_user("test_provider", "different_user", _params, _user_id_params), do: {:error, {:bound_to_different_user, %{}}} + + def delete(@user, "test_provider"), do: {:ok, {1, nil}} + def delete(:error, "test_provider"), do: {:error, :error} + def delete(:no_password, "test_provider"), do: {:error, {:no_password, nil}} + + def all(@user), do: [@user_identity] + + def user, do: @user +end diff --git a/test/test_helper.exs b/test/test_helper.exs index 05997dc..8ccbbce 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1,4 +1,3 @@ -Mix.shell(Mix.Shell.Process) Logger.configure(level: :warn) ExUnit.start() @@ -19,4 +18,4 @@ Mix.Task.run("ecto.migrate", ~w(--quiet -r PowAssent.Test.Ecto.Repo)) Ecto.Adapters.SQL.Sandbox.mode(PowAssent.Test.Ecto.Repo, :manual) {:ok, _pid} = PowAssent.Test.Phoenix.Endpoint.start_link() -{:ok, _pid} = PowAssent.Test.Phoenix.MailerEndpoint.start_link() +{:ok, _pid} = PowAssent.Test.Phoenix.EndpointConfirmEmail.start_link()