caslu

caslu

Testing pow authenticated controllers

I added Pow to my application using this guide because i have a JSON api structure, but after integrate with pow, my controller tests broke because neither of them needed to have a session before and now they do. The problem is that i’m not being able to make the tests work, my approach so far was change the default setup method generated by phoenix to this:

# before
setup %{conn: conn} do
 {:ok, conn: put_req_header(conn, "accept", "application/json")}
end

# now
setup %{conn: conn} do
  user = user_fixture()

  authed_conn =
    conn
    |> put_req_header("accept", "application/json")
    |> pow.plug.assign_current_user(user, [])

  {:ok, conn: authed_conn}
end

Now the index test pass successfully, but the others give me this error message:

** (RuntimeError) expected connection to have a response but no response was set/sent.
     Please verify that you assign to "conn" after a request:

       conn = get(conn, "/")
       assert html_response(conn) =~ "Hello"
  
   code: assert json_response(conn, 422)["errors"] != %{}
   stacktrace:
     (phoenix 1.7.12) lib/phoenix/test/conn_test.ex:358: Phoenix.ConnTest.response/2
     (phoenix 1.7.12) lib/phoenix/test/conn_test.ex:419: Phoenix.ConnTest.json_response/2
     test/ponto_cao_web/controllers/pet_controller_test.exs:106: (test)

I didn’t find in pow docs and repo some recommended way to tests authenticated controllers on API only projects, can you guys help me to understand why this is happening ?

Marked As Solved

LostKobrakai

LostKobrakai

A conn knows and stores if it was already used to make a request (iirc conn.state). So your two approaches are.

# conn was used before
conn = 
  conn 
  # assign current user to the used conn
  |> Pow.Plug.assign_current_user(user, []) 
  # get/2 detects the conn as already being used and recycles it,
  # which doesn't retain the assign you added
  |> get(~p"/api/events/#{id}")

and

# conn was used before
conn = 
  build_conn()
  # assign current user to the used conn
  |> Pow.Plug.assign_current_user(user, []) 
  # get/2 detects the conn as not used yet, so no recycling is happening
  # the assign stays
  |> get(~p"/api/events/#{id}")

You could do conn |> recycle() |> Pow.Plug.assign_current_user(user, []) to work around the automatic recycling happing at an inconvencient time.

You could also consider not writing to conn.assigns, but to put the authentication details in the session. That way authentication will persist across recycles just like it does persist across many requests outside of testing.

Also Liked

LostKobrakai

LostKobrakai

Recycling a conn tries to do essentially the same as what happens when a browser would navigate to a new route. Only things like cookies, some headers, hostname, … will be retained. assigns are not retained between multiple requests being made, but need to be computed and set freshly for each request, so the same applies with recycles.

Here you have a conn, which was already used to do a request, then you assign some data and then you request a new page, trigger a recycle and therefore get rid of tje assign again.

If you do multiple request you likely want to authenticate, so that a session is started and not just for a single request by setting just assigns.

Hermanverschooten

Hermanverschooten

Haven’t used pow myself, but I guess you should do something like they do in their tests,
conn |> PowPlug.assign_current_user(@user, [])

https://github.com/pow-auth/pow/blob/801408e7ce74ecfd85aae1ecc0380187ddb25c3c/test/extensions/reset_password/phoenix/controllers/reset_password_controller_test.exs#L32

Hermanverschooten

Hermanverschooten

Could you show your complete test?

Last Post!

habutre

habutre

I was facing the same issue and what worked for me instead of recycling the conn was to take advantage of immutability and not reuse the authenticated connection from setup

I named the conn from context as conn_auth, use it in my calls e.g. conn = get(conn_auth,…)

So my authenticated connection was kept untouched and I could assert my responses normally and as much as I need

Where Next?

Popular in Questions Top

Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New
sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
marius95
Hello everyone, I try to use an Javascript Event Handler in my root.html.leex file. Therefore I created a function in the app.js file: ...
New

Other popular topics Top

minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 40082 209
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New

We're in Beta

About us Mission Statement