I have written a clojure webapp which looks very much like
the example webapp given as the friend exmaple. I have
written a little test using ring-mock
to test if the authentication
works properly. Writing this test I found some behaviour I do not understand.
See the following code as an example.
(let [route "/login"
login-post (body (request :post route)
{"username" "Username" "password" "Password"})]
(println (secured-app login-post))
(println (secured-app login-post)))
The first println
returns the correct result:
{:status 303,
:headers
{"Set-Cookie"
("ring-session=ENCRYPTED-STUFF;Path=/"),
"Location" "/"},
:body ""}
But the second one returns a login error:
{:status 302,
:headers
{"Location" "http://localhost/login?&login_failed=Y&username="},
:body ""}
Somewhere in this code there is state being "transmitted" from the first
request being handled to the second request (secured-app login-post)
but I don't see it.
When I for example run this statement:
(println (secured-app(body (request :post "/login")
{"username" "Username" "password" "Password"})))
in the repl multiple times, I always get the first proper result.
Where does this behaviour come from and where is the state handeled in this example?
The
ring.mock.request/body
function turns the given body data into a mutable ByteArrayInputStream. This means that normally, once your request has been passed through the app, the body stream has been read (usually byring.middleware.params/assoc-form-params
, which callsslurp
on the request body) and is now empty.In other words, if you're testing a ring app with ring-mock, you should never reuse a request with a body. It's probably best to always create a new request.