I have a login requirement plug that pretty much resembles the one described in this example by Thoughtbot. I want to add a flash notification when the user is redirected. It works in the browser, but not when testing it in isolation.
The plug:
# In webs/plugs/require_login.ex
defmodule MyApp.Plugs.RequireLogin do
import Plug.Conn
def init(opts), do: opts
if false # real user authentication omitted
conn
else
conn
|> Phoenix.Controller.put_flash(:error, "Login required.")
|> Phoenix.Controller.redirect(to: "/")
|> halt
end
end
end
The test used for this:
defmodule MyApp.Plugs.RequireLoginTest do
use MyApp.ConnCase
test "user is redirected when authentication fails" do
conn = conn |> MyApp.Plugs.RequireLogin.call(%{})
assert Phoenix.Controller.get_flash(conn, :error) == "Login required."
assert redirected_to(conn) == "/"
end
end
The error message I get is:
(ArgumentError) flash not fetched, call fetch_flash/2
The error occurs in the plug module but if I comment out the put_session
line there, the error moves to my test file.
I understand the session store is configured in lib/my_app/endpoint.ex
but how can I re-use this configuration so that I can unit-test plugs?
Here's how the plug is hooked into the router:
# web/router.ex
pipeline :browser do
# the Phoenix default
end
scope "/", MyApp do
pipe_through [:browser, MyApp.Plugs.RequireLogin]
resource "/protected", MyController, only: [:index]
end
I was able to solve this using bypass_through. I did not "fire" a request using e.g. Phoenix.ConnTest.get/3
, resulting in the plug pipeline not being called. As soon as I added get/3
, the flash hash became available.
defmodule MyApp.Plugs.RequireLoginTest do
use MyApp.ConnCase
test "user is redirected when authentication fails", %{conn: conn} do
conn = conn
|> with_pipeline
|> MyApp.Plugs.RequireLogin.call(%{})
assert Phoenix.Controller.get_flash(conn, :error) == "Login required."
assert redirected_to(conn) == "/"
end
defp with_pipeline(conn) do
conn
|> bypass_through(MyApp.Router, [:browser])
|> get("/protected-uri") # any uri, bypass_through ignores the router rules
end
end