Question

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?

Was it helpful?

Solution

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 by ring.middleware.params/assoc-form-params, which calls slurp 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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top