Guardian in controller tests

Hello all, I am trying to test controllers that require Guardian authentication (that I’m trying to fake).
My first approach was super complex and based on what I found in some other examples, for example:

I gave up because it gives problems one way or another.

Now I’m trying to simplify my approach to how it should really work in my opinion:

When signing the connection directly it still fails because “session not fetched, call fetch_session/2”, basically what I was doing before when getting “/”
I guess this is pretty common when using guardian, does anybody have any code to share? How are you testing controllers protected by guardian?

ngw

2 Likes

This is something I ran into recently and figured out from this stackoverflow answer as well as the linked test in Phoenix http://stackoverflow.com/a/31983168.

After setting up the session, Guardian.Plug.sign_in in your test should be able to use the session as needed.

To solve this in my own case, I use a helper which I defined in conn_case.ex. The trick is to flush your session so subsequent requests contain the correct session data for Guardian to do its thing.

@doc """
Helper for setting up a `conn` which logs in a user
"""
def guardian_login(user, token \\ :token, opts \\ []) do
  build_conn
  |> bypass_through(YourApp.Router, [:browser, :browser_authenticated_session])
  |> get("/")
  |> Guardian.Plug.sign_in(user, token, opts)
  |> send_resp(200, "Flush the session")
  |> recycle
end

For example

test "a thing that requires a logged in user" do
  user = insert(:user)
  conn = guardian_login(user)

  conn = get(conn, path_requiring_authorisation(conn, :index))
  assert html_response(conn, 200)
end

If you’re writing multiple tests which require a logged in user, consider building conn in a setup block and pass that down to your tests. In this example, conn which is passed down to the test in the background is not pattern matched on as we’re assigning our own conn from scratch.

3 Likes

I’m sorry for bumping this old thread but I’m stuck with the following error while trying to test a controller that requires the authentication through Guardian:

** (Plug.Conn.AlreadySentError) the response was already sent

The project is a Phoenix 1.3 with Guardian and Ueberauth for the authentication. Here’s the repo: https://github.com/fusillicode/metro

The error can be easily reproduced by launching the following test:

mix test test/metro/web/controllers/admin/media_controller_test.exs:15

I tried to debug the conn but sincerely I wasn’t able to find anything that could help me fix the problem :disappointed:

I fear I’m just missing something stupid (…and I also feel kind of stupid myself :sob:)

1 Like

I have the same problem as @fusillicode, when trying to move my application to Phoenix 1.3 file structure.
It seems, I need to move back to the old one :frowning:

Hey, I found a hack to solve the problem.
Just add Map.update!(:state, fn (_) -> :set end), before Guardian.Plug.sign_in(user, token, opts).

Yes, I confirm, your suggestion fixes the problem :hugs: :tada:

Actually I was able to identify the problem, i.e. the value of the :state key of the conn, but I got lost while trying to understand the reason of the problem.

I hope to have some time to further investigate it.

In the meanwhile let me thank you for your support! :hugs:

Cheers!