Testing controllers - unexpected problems with custom Pow auth

Hi, during creating my own custom Pow auth I stuck during testing. I made a research on web but couldnt find solution.

For learning purpose I show token in json. I need it to paste in postman during manual testing (i dont know yet how to use postman with session/cookies auth :frowning: )

@spec reset_password(Conn.t(), map()) :: Conn.t() def reset_password(conn, %{"id" => token, "user" => user_params}) do with {:ok, conn} <- PowResetPassword.Plug.load_user_by_token(conn, token), {:ok, _user, conn} <- PowResetPassword.Plug.update_user_password(conn, user_params) do json(conn, %{status: "Password changed"}) else {:error, _changeset, conn} -> json(conn, %{error: %{message: "Passwords are not the same"}}) _ -> json(conn, %{error: %{message: "Expired Token"}}) end end 

Function works well, shows expected errors while providing invalid params or token. I wanted to write tests for it and then I stuck. I cant go through them and my every try is failed.

I setup up user before tests:

 setup do user = %User{} |> User.changeset(%{email: "test@example.com", password: @password, password_confirmation: @password}) |> Repo.insert!() {:ok, user: user} end 

First attemp:

 @valid_params %{"id" => "token", "user" => %{"password" => @new_password, "password_confirmation" => @new_password}} describe "reset_password/2" do test "with valid token and passwords", %{conn: conn} do conn = post(conn, Routes.password_path(conn, :reset_password, @valid_params)) assert json = json_response(conn, 200) assert json["status"] end end 

Error in terminal:

Expected truthy, got nil code: assert json["status"] arguments: # 1 %{"error" => %{"message" => "Expired Token"}} # 2 "status" 

I suppose that it needs real token, not any hard coded pretending to be real.

Second attemp:

 use PowApiTemplateWeb.ConnCase alias Plug.Conn alias PowApiTemplate.{Repo, Users.User} alias PowResetPassword.Plug alias Pow.Plug, as: PowPlug describe "reset_password/2" do setup %{conn: conn} do token = PowResetPassword.Plug.create_reset_token(conn, "test@example.com") {:ok, conn: conn, token: token} end test "with valid token and passwords", %{conn: conn} do conn = post(conn, Routes.password_path(conn, :reset_password, @valid_params)) assert json = json_response(conn, 200) assert json["status"] end end 

It seems to be working. I use real conn, use function providing token, but there appears an error i couldn’t resolve even if I checked for solutions, check if I write in config everything like it is written in docs:

(Pow.Config.ConfigError) Pow configuration not found in connection. Please use a Pow plug that puts the Pow configuration in the plug connection. 

I tried also different way of writing this func but Pow config errors still appears. I dont think my function in untestable, but there are issues I can resolve after digging in docs:

 describe "reset_password/2" do test "with valid token and passwords", %{conn: conn} do token = PowResetPassword.Plug.create_reset_token(conn, "test@example.com") conn = post(conn, Routes.password_path(conn, :reset_password, %{"id" => token, "user" => %{"password" => @new_password, "password_confirmation" => @new_password}})) assert json = json_response(conn, 200) assert json["status"] end end 

Thanks for any help :slight_smile:

I made quite big step further. Still I have errors but with ETS.

I added to test folder Ets Cache Mock:

defmodule PowApiTemplate.Test.EtsCacheMock do @moduledoc false @tab __MODULE__ def init, do: :ets.new(@tab, [:ordered_set, :protected, :named_table]) def get(config, key) do ets_key = ets_key(config, key) @tab |> :ets.lookup(ets_key) |> case do [{^ets_key, value} | _rest] -> value [] -> :not_found end end def delete(config, key) do :ets.delete(@tab, ets_key(config, key)) :ok end def put(config, record_or_records) do records = List.wrap(record_or_records) ets_records = Enum.map(records, fn {key, value} -> {ets_key(config, key), value} end) send(self(), {:ets, :put, records, config}) :ets.insert(@tab, ets_records) end def all(config, match) do ets_key_match = ets_key(config, match) @tab |> :ets.select([{{ets_key_match, :_}, [], [:"$_"]}]) |> Enum.map(fn {[_namespace | keys], value} -> {keys, value} end) end defp ets_key(config, key) do [Keyword.get(config, :namespace, "cache")] ++ List.wrap(key) end end 

Updated test config with:

config :pow_api_template, :pow, cache_backend: [cache_store_backend: PowApiTemplate.Test.EtsCacheMock] 

Error is the same if I change it to:

config :pow_api_template, :pow, cache_store_backend: PowApiTemplate.Test.EtsCacheMock 

I updated conn_case.ex with:

 setup _tags do EtsCacheMock.init() {:ok, conn: Phoenix.ConnTest.build_conn(), ets: EtsCacheMock} end 

Adding this line to test_helper.exs leads to fail all tests - even with registration/session controllers:

PowApiTemplate.Test.EtsCacheMock.init()

I also updated password_controller_test.exs:

 setup do user = %User{} |> User.changeset(%{email: "test@example.com", password: @password, password_confirmation: @password}) |> Repo.insert!() {:ok, user: user} end describe "reset_password/2" do test "with valid token and passwords", %{conn: conn} do PowApiTemplate.Test.EtsCacheMock.init() pow_config = [otp_app: :pow_api_template] {:ok, %{token: token, user: user}, conn} = conn |> Pow.Plug.put_config(Application.get_env(:pow_api_template, :pow)) |> PowResetPassword.Plug.create_reset_token(%{"email" => "test@example.com"}) valid_params = %{"id" => token, "user" => %{"password" => @new_password, "password_confirmation" => @new_password}} conn = post(conn, Routes.password_path(conn, :reset_password, valid_params)) assert json = json_response(conn, 200) end end 

Trying to start test leads to error:

1) test reset_password/2 with valid token and passwords (PowApiTemplateWeb.PasswordControllerTest) test/pow_api_template_web/controllers/password_controller_test.exs:45 ** (ArgumentError) errors were found at the given arguments: * 2nd argument: invalid options code: PowApiTemplate.Test.EtsCacheMock.init() stacktrace: (stdlib 4.0.1) :ets.new(PowApiTemplate.Test.EtsCacheMock, [:set, :protected, :named_table]) (pow_api_template 0.1.0) test/support/ets_cache_mock.ex:5: PowApiTemplate.Test.EtsCacheMock.init/0 test/pow_api_template_web/controllers/password_controller_test.exs:46: (test) 

I also changed :set to :ordered_set but nothing changes with error. Maybe in Pow 1.0.27 there are changes that I didnt implemented or I wrote something incorrectly. I’ll check later PowApiTemplate.Test.EtsCacheMock.init() function and I will try to play with options.